 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
  * Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
  * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
  * Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
  *******************************************************************************/
 package org.eclipse.ui.texteditor;


 import java.lang.reflect.InvocationTargetException ;
 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;
 import java.util.ResourceBundle ;

 import org.osgi.framework.Bundle;

 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.custom.ST;
 import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.custom.VerifyKeyListener;
 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.dnd.DragSource;
 import org.eclipse.swt.dnd.DragSourceAdapter;
 import org.eclipse.swt.dnd.DragSourceEvent;
 import org.eclipse.swt.dnd.DropTargetAdapter;
 import org.eclipse.swt.dnd.DropTargetEvent;
 import org.eclipse.swt.dnd.DropTargetListener;
 import org.eclipse.swt.dnd.TextTransfer;
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.KeyListener;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseListener;
 import org.eclipse.swt.events.VerifyEvent;
 import org.eclipse.swt.events.VerifyListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.FontData;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.graphics.PaletteData;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.widgets.Caret;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Shell;

 import org.eclipse.core.commands.operations.IOperationApprover;
 import org.eclipse.core.commands.operations.IOperationHistory;
 import org.eclipse.core.commands.operations.IUndoContext;
 import org.eclipse.core.commands.operations.OperationHistoryFactory;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.ILog;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.core.runtime.Status;

 import org.eclipse.text.undo.DocumentUndoManagerRegistry;
 import org.eclipse.text.undo.IDocumentUndoManager;

 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.GroupMarker;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.preference.PreferenceConverter;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.jface.util.SafeRunnable;
 import org.eclipse.jface.viewers.IPostSelectionProvider;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.window.IShellProvider;

 import org.eclipse.jface.text.AbstractInformationControlManager;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.DefaultInformationControl;
 import org.eclipse.jface.text.DefaultLineTracker;
 import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentListener;
 import org.eclipse.jface.text.IFindReplaceTarget;
 import org.eclipse.jface.text.IFindReplaceTargetExtension;
 import org.eclipse.jface.text.IInformationControl;
 import org.eclipse.jface.text.IInformationControlCreator;
 import org.eclipse.jface.text.IMarkRegionTarget;
 import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.IRewriteTarget;
 import org.eclipse.jface.text.ISelectionValidator;
 import org.eclipse.jface.text.ITextHover;
 import org.eclipse.jface.text.ITextInputListener;
 import org.eclipse.jface.text.ITextListener;
 import org.eclipse.jface.text.ITextOperationTarget;
 import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.ITextViewerExtension;
 import org.eclipse.jface.text.ITextViewerExtension2;
 import org.eclipse.jface.text.ITextViewerExtension4;
 import org.eclipse.jface.text.ITextViewerExtension5;
 import org.eclipse.jface.text.ITextViewerExtension6;
 import org.eclipse.jface.text.ITextViewerExtension7;
 import org.eclipse.jface.text.IUndoManager;
 import org.eclipse.jface.text.IUndoManagerExtension;
 import org.eclipse.jface.text.Position;
 import org.eclipse.jface.text.Region;
 import org.eclipse.jface.text.TabsToSpacesConverter;
 import org.eclipse.jface.text.TextEvent;
 import org.eclipse.jface.text.TextSelection;
 import org.eclipse.jface.text.TextUtilities;
 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
 import org.eclipse.jface.text.information.IInformationProvider;
 import org.eclipse.jface.text.information.IInformationProviderExtension;
 import org.eclipse.jface.text.information.IInformationProviderExtension2;
 import org.eclipse.jface.text.information.InformationPresenter;
 import org.eclipse.jface.text.link.LinkedModeModel;
 import org.eclipse.jface.text.link.LinkedPosition;
 import org.eclipse.jface.text.revisions.RevisionInformation;
 import org.eclipse.jface.text.source.Annotation;
 import org.eclipse.jface.text.source.CompositeRuler;
 import org.eclipse.jface.text.source.IAnnotationHover;
 import org.eclipse.jface.text.source.IAnnotationHoverExtension;
 import org.eclipse.jface.text.source.IAnnotationModel;
 import org.eclipse.jface.text.source.ILineRange;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jface.text.source.ISourceViewerExtension3;
 import org.eclipse.jface.text.source.IVerticalRuler;
 import org.eclipse.jface.text.source.IVerticalRulerColumn;
 import org.eclipse.jface.text.source.IVerticalRulerExtension;
 import org.eclipse.jface.text.source.IVerticalRulerInfo;
 import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.jface.text.source.SourceViewerConfiguration;
 import org.eclipse.jface.text.source.VerticalRuler;

 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IEditorDescriptor;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IEditorRegistry;
 import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.IKeyBindingService;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.INavigationLocation;
 import org.eclipse.ui.INavigationLocationProvider;
 import org.eclipse.ui.IPartListener;
 import org.eclipse.ui.IPartService;
 import org.eclipse.ui.IPersistableEditor;
 import org.eclipse.ui.IReusableEditor;
 import org.eclipse.ui.ISaveablesLifecycleListener;
 import org.eclipse.ui.ISaveablesSource;
 import org.eclipse.ui.IWindowListener;
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.Saveable;
 import org.eclipse.ui.SaveablesLifecycleEvent;
 import org.eclipse.ui.actions.CommandNotMappedException;
 import org.eclipse.ui.actions.ContributedAction;
 import org.eclipse.ui.dialogs.PropertyDialogAction;
 import org.eclipse.ui.dnd.IDragAndDropService;
 import org.eclipse.ui.internal.texteditor.EditPosition;
 import org.eclipse.ui.internal.texteditor.NLSUtility;
 import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
 import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
 import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
 import org.eclipse.ui.operations.NonLocalUndoUserApprover;
 import org.eclipse.ui.operations.OperationHistoryActionHandler;
 import org.eclipse.ui.operations.RedoActionHandler;
 import org.eclipse.ui.operations.UndoActionHandler;
 import org.eclipse.ui.part.EditorPart;
 import org.eclipse.ui.texteditor.rulers.IColumnSupport;
 import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
 import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
 import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
 import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;


 /**
  * Abstract base implementation of a text editor.
  * <p>
  * Subclasses are responsible for configuring the editor appropriately.
  * The standard text editor, <code>TextEditor</code>, is one such example.</p>
  * <p>
  * If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the argument is
  * used as the id under which the editor's context menu is registered for extensions.
  * If no id is set, the context menu is registered under <b>[editor_id].EditorContext</b>
  * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
  * run in version 1.0 context menu registration compatibility mode, the latter form of the
  * registration even happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}.
  * If no id is set while in compatibility mode, the menu is registered under
  * {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.</p>
  * <p>
  * If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the argument is
  * used as the id under which the ruler's context menu is registered for extensions.
  * If no id is set, the context menu is registered under <b>[editor_id].RulerContext</b>
  * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
  * run in version 1.0 context menu registration compatibility mode, the latter form of the
  * registration even happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId}.
  * If no id is set while in compatibility mode, the menu is registered under
  * {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.</p>
  */
 public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, INavigationLocationProvider, ISaveablesSource, IPersistableEditor {

     /**
      * Tag used in xml configuration files to specify editor action contributions.
      * Current value: <code>editorContribution</code>
      * @since 2.0
      */
     private static final String TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$

     /**
      * Tags used in the {@link IMemento} when saving and
      * restoring editor state.
      *
      * @see #saveState(IMemento)
      * @see #restoreState(IMemento)
      * @since 3.3
      */
     protected static final String TAG_SELECTION_OFFSET= "selectionOffset"; //$NON-NLS-1$
 protected static final String TAG_SELECTION_LENGTH= "selectionLength"; //$NON-NLS-1$
 // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
 private static final String TAG_SELECTION_HPIXEL= "selectionHPixel"; //$NON-NLS-1$


     /**
      * The caret width for the wide (double) caret.
      * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
      * Value: {@value}
      * @since 3.0
      */
     private static final int WIDE_CARET_WIDTH= 2;

     /**
      * The caret width for the narrow (single) caret.
      * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
      * Value: {@value}
      * @since 3.0
      */
     private static final int SINGLE_CARET_WIDTH= 1;

     /**
      * The text input listener.
      *
      * @see ITextInputListener
      * @since 2.1
      */
     private static class TextInputListener implements ITextInputListener {
         /** Indicates whether the editor input changed during the process of state validation. */
         public boolean inputChanged;

         /* Detectors for editor input changes during the process of state validation. */
         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {}
         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { inputChanged= true; }
     }

     /**
      * Internal element state listener.
      */
     class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {

             /**
              * Internal <code>VerifyListener</code> for performing the state validation of the
              * editor input in case of the first attempted manipulation via typing on the keyboard.
              * @since 2.0
              */
             class Validator implements VerifyListener {
                 /*
                  * @see VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
                  */
                 public void verifyText(VerifyEvent e) {
                     IDocument document= getDocumentProvider().getDocument(getEditorInput());
                     final boolean[] documentChanged= new boolean[1];
                     IDocumentListener listener= new IDocumentListener() {
                         public void documentAboutToBeChanged(DocumentEvent event) {
                         }
                         public void documentChanged(DocumentEvent event) {
                             documentChanged[0]= true;
                         }
                     };
                     try {
                         if (document != null)
                             document.addDocumentListener(listener);
                         if (! validateEditorInputState() || documentChanged[0])
                             e.doit= false;
                     } finally {
                         if (document != null)
                             document.removeDocumentListener(listener);
                     }
                 }
             }

         /**
          * The listener's validator.
          * @since 2.0
          */
         private Validator fValidator;
         /**
          * The display used for posting runnable into the UI thread.
          * @since 3.0
          */
         private Display fDisplay;

         /*
          * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean)
          * @since 2.0
          */
         public void elementStateValidationChanged(final Object element, final boolean isStateValidated) {
             if (element != null && element.equals(getEditorInput())) {
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);
                         if (isStateValidated && fValidator != null) {
                             ISourceViewer viewer= fSourceViewer;
                             if (viewer != null) {
                                 StyledText textWidget= viewer.getTextWidget();
                                 if (textWidget != null && !textWidget.isDisposed())
                                     textWidget.removeVerifyListener(fValidator);
                                 fValidator= null;
                                 enableStateValidation(false);
                             }
                         } else if (!isStateValidated && fValidator == null) {
                             ISourceViewer viewer= fSourceViewer;
                             if (viewer != null) {
                                 StyledText textWidget= viewer.getTextWidget();
                                 if (textWidget != null && !textWidget.isDisposed()) {
                                     fValidator= new Validator();
                                     enableStateValidation(true);
                                     textWidget.addVerifyListener(fValidator);
                                 }
                             }
                         }
                     }
                 };
                 execute(r, false);
             }
         }


         /*
          * @see IElementStateListener#elementDirtyStateChanged(Object, boolean)
          */
         public void elementDirtyStateChanged(Object element, boolean isDirty) {
             if (element != null && element.equals(getEditorInput())) {
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);
                         firePropertyChange(PROP_DIRTY);
                     }
                 };
                 execute(r, false);
             }
         }

         /*
          * @see IElementStateListener#elementContentAboutToBeReplaced(Object)
          */
         public void elementContentAboutToBeReplaced(Object element) {
             if (element != null && element.equals(getEditorInput())) {
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);
                         rememberSelection();
                         resetHighlightRange();
                     }
                 };
                 execute(r, false);
             }
         }

         /*
          * @see IElementStateListener#elementContentReplaced(Object)
          */
         public void elementContentReplaced(Object element) {
             if (element != null && element.equals(getEditorInput())) {
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);
                         firePropertyChange(PROP_DIRTY);
                         restoreSelection();
                         handleElementContentReplaced();
                     }
                 };
                 execute(r, false);
             }
         }

         /*
          * @see IElementStateListener#elementDeleted(Object)
          */
         public void elementDeleted(Object deletedElement) {
             if (deletedElement != null && deletedElement.equals(getEditorInput())) {
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);
                         close(false);
                     }
                 };
                 execute(r, false);
             }
         }

         /*
          * @see IElementStateListener#elementMoved(Object, Object)
          */
         public void elementMoved(final Object originalElement, final Object movedElement) {
             if (originalElement != null && originalElement.equals(getEditorInput())) {
                 final boolean doValidationAsync= Display.getCurrent() != null;
                 Runnable r= new Runnable () {
                     public void run() {
                         enableSanityChecking(true);

                         if (fSourceViewer == null)
                             return;

                         if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) {
                             close(true);
                             return;
                         }

                         if (movedElement == null || movedElement instanceof IEditorInput) {
                             rememberSelection();

                             final IDocumentProvider d= getDocumentProvider();
                             final String previousContent;
                             IDocumentUndoManager previousUndoManager=null;
                             IDocument changed= null;
                             boolean wasDirty= isDirty();
                             changed= d.getDocument(getEditorInput());
                             if (changed != null) {
                                 if (wasDirty)
                                     previousContent= changed.get();
                                 else
                                     previousContent= null;
                                 
                                 previousUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(changed);
                                 if (previousUndoManager != null)
                                     previousUndoManager.connect(this);
                             }
                             else
                                 previousContent= null;
                             
                             setInput((IEditorInput) movedElement);
                             
                             // The undo manager needs to be replaced with one for the new document.
 // Transfer the undo history and then disconnect from the old undo manager.
 if (previousUndoManager != null) {
                                 IDocument newDocument= getDocumentProvider().getDocument(movedElement);
                                 if (newDocument != null) {
                                     IDocumentUndoManager newUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(newDocument);
                                     if (newUndoManager != null)
                                         newUndoManager.transferUndoHistory(previousUndoManager);
                                 }
                                 previousUndoManager.disconnect(this);
                             }

                             if (wasDirty && changed != null) {
                                 Runnable r2= new Runnable () {
                                     public void run() {
                                         validateState(getEditorInput());
                                         d.getDocument(getEditorInput()).set(previousContent);
                                         updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
                                         restoreSelection();
                                     }
                                 };
                                 execute(r2, doValidationAsync);
                             } else
                                 restoreSelection();

                         }
                     }
                 };
                 execute(r, false);
             }
         }

         /*
          * @see IElementStateListenerExtension#elementStateChanging(Object)
          * @since 2.0
          */
         public void elementStateChanging(Object element) {
             if (element != null && element.equals(getEditorInput()))
                 enableSanityChecking(false);
         }

         /*
          * @see IElementStateListenerExtension#elementStateChangeFailed(Object)
          * @since 2.0
          */
         public void elementStateChangeFailed(Object element) {
             if (element != null && element.equals(getEditorInput()))
                 enableSanityChecking(true);
         }

         /**
          * Executes the given runnable in the UI thread.
          * <p>
          * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
          * about why the parameter <code>postAsync</code> has been
          * introduced in the course of 3.1.
          *
          * @param runnable runnable to be executed
          * @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
          * @since 3.0
          */
         private void execute(Runnable runnable, boolean postAsync) {
             if (postAsync || Display.getCurrent() == null) {
                 if (fDisplay == null)
                     fDisplay= getSite().getShell().getDisplay();
                 fDisplay.asyncExec(runnable);
             } else
                 runnable.run();
         }
     }

     /**
      * Internal text listener for updating all content dependent
      * actions. The updating is done asynchronously.
      */
     class TextListener implements ITextListener, ITextInputListener {

         /** The posted updater code. */
         private Runnable fRunnable= new Runnable () {
             public void run() {
                 fIsRunnablePosted= false;

                 if (fSourceViewer != null) {
                     updateContentDependentActions();

                     // remember the last edit position
 if (isDirty() && fUpdateLastEditPosition) {
                         fUpdateLastEditPosition= false;
                         ISelection sel= getSelectionProvider().getSelection();
                         IEditorInput input= getEditorInput();
                         IDocument document= getDocumentProvider().getDocument(input);

                         if (fLocalLastEditPosition != null) {
                             document.removePosition(fLocalLastEditPosition);
                             fLocalLastEditPosition= null;
                         }

                         if (sel instanceof ITextSelection && !sel.isEmpty()) {
                             ITextSelection s= (ITextSelection) sel;
                             fLocalLastEditPosition= new Position(s.getOffset(), s.getLength());
                             try {
                                 document.addPosition(fLocalLastEditPosition);
                             } catch (BadLocationException ex) {
                                 fLocalLastEditPosition= null;
                             }
                         }
                         TextEditorPlugin.getDefault().setLastEditPosition(new EditPosition(input, getEditorSite().getId(), fLocalLastEditPosition));
                     }
                 }
             }
         };

         /** Display used for posting the updater code. */
         private Display fDisplay;
         /**
          * The editor's last edit position
          * @since 3.0
          */
         private Position fLocalLastEditPosition;
         /**
          * Has the runnable been posted?
          * @since 3.0
          */
         private boolean fIsRunnablePosted= false;
         /**
          * Should the last edit position be updated?
          * @since 3.0
          */
         private boolean fUpdateLastEditPosition= false;

         /*
          * @see ITextListener#textChanged(TextEvent)
          */
         public void textChanged(TextEvent event) {

             /*
              * Also works for text events which do not base on a DocumentEvent.
              * This way, if the visible document of the viewer changes, all content
              * dependent actions are updated as well.
              */

             if (fDisplay == null)
                 fDisplay= getSite().getShell().getDisplay();

             if (event.getDocumentEvent() != null)
                 fUpdateLastEditPosition= true;

             if (!fIsRunnablePosted) {
                 fIsRunnablePosted= true;
                 fDisplay.asyncExec(fRunnable);
             }
         }

         /*
          * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
          */
         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
             if (oldInput != null && fLocalLastEditPosition != null) {
                 oldInput.removePosition(fLocalLastEditPosition);
                 fLocalLastEditPosition= null;
             }
         }

         /*
          * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
          */
         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
         }
     }

     /**
      * Internal property change listener for handling changes in the editor's preferences.
      */
     class PropertyChangeListener implements IPropertyChangeListener {
         /*
          * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
          */
         public void propertyChange(PropertyChangeEvent event) {
             handlePreferenceStoreChanged(event);
         }
     }

     /**
      * Internal property change listener for handling workbench font changes.
      * @since 2.1
      */
     class FontPropertyChangeListener implements IPropertyChangeListener {
         /*
          * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
          */
         public void propertyChange(PropertyChangeEvent event) {
             if (fSourceViewer == null)
                 return;

             String property= event.getProperty();

             if (getFontPropertyPreferenceKey().equals(property)) {
                 initializeViewerFont(fSourceViewer);
                 updateCaret();
             }
         }
     }

     /**
      * Internal key verify listener for triggering action activation codes.
      */
     class ActivationCodeTrigger implements VerifyKeyListener {

         /** Indicates whether this trigger has been installed. */
         private boolean fIsInstalled= false;
         /**
          * The key binding service to use.
          * @since 2.0
          */
         private IKeyBindingService fKeyBindingService;

         /*
          * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
          */
         public void verifyKey(VerifyEvent event) {

             ActionActivationCode code= null;
             int size= fActivationCodes.size();
             for (int i= 0; i < size; i++) {
                 code= (ActionActivationCode) fActivationCodes.get(i);
                 if (code.matches(event)) {
                     IAction action= getAction(code.fActionId);
                     if (action != null) {

                         if (action instanceof IUpdate)
                             ((IUpdate) action).update();

                         if (!action.isEnabled() && action instanceof IReadOnlyDependent) {
                             IReadOnlyDependent dependent= (IReadOnlyDependent) action;
                             boolean writable= dependent.isEnabled(true);
                             if (writable) {
                                 event.doit= false;
                                 return;
                             }
                         } else if (action.isEnabled()) {
                             event.doit= false;
                             action.run();
                             return;
                         }
                     }
                 }
             }
         }

         /**
          * Installs this trigger on the editor's text widget.
          * @since 2.0
          */
         public void install() {
             if (!fIsInstalled) {

                 if (fSourceViewer instanceof ITextViewerExtension) {
                     ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
                     e.prependVerifyKeyListener(this);
                 } else {
                     StyledText text= fSourceViewer.getTextWidget();
                     text.addVerifyKeyListener(this);
                 }

                 fKeyBindingService= getEditorSite().getKeyBindingService();
                 fIsInstalled= true;
             }
         }

         /**
          * Uninstalls this trigger from the editor's text widget.
          * @since 2.0
          */
         public void uninstall() {
             if (fIsInstalled) {

                 if (fSourceViewer instanceof ITextViewerExtension) {
                     ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
                     e.removeVerifyKeyListener(this);
                 } else if (fSourceViewer != null) {
                     StyledText text= fSourceViewer.getTextWidget();
                     if (text != null && !text.isDisposed())
                         text.removeVerifyKeyListener(fActivationCodeTrigger);
                 }

                 fIsInstalled= false;
                 fKeyBindingService= null;
             }
         }

         /**
          * Registers the given action for key activation.
          * @param action the action to be registered
          * @since 2.0
          */
         public void registerActionForKeyActivation(IAction action) {
             if (action.getActionDefinitionId() != null)
                 fKeyBindingService.registerAction(action);
         }

         /**
          * The given action is no longer available for key activation
          * @param action the action to be unregistered
          * @since 2.0
          */
         public void unregisterActionFromKeyActivation(IAction action) {
             if (action.getActionDefinitionId() != null)
                 fKeyBindingService.unregisterAction(action);
         }

         /**
          * Sets the key binding scopes for this editor.
          * @param keyBindingScopes the key binding scopes
          * @since 2.1
          */
         public void setScopes(String [] keyBindingScopes) {
             if (keyBindingScopes != null && keyBindingScopes.length > 0)
                 fKeyBindingService.setScopes(keyBindingScopes);
         }
     }

     /**
      * Representation of action activation codes.
      */
     static class ActionActivationCode {

         /** The action id. */
         public String fActionId;
         /** The character. */
         public char fCharacter;
         /** The key code. */
         public int fKeyCode= -1;
         /** The state mask. */
         public int fStateMask= SWT.DEFAULT;

         /**
          * Creates a new action activation code for the given action id.
          * @param actionId the action id
          */
         public ActionActivationCode(String actionId) {
             fActionId= actionId;
         }

         /**
          * Returns <code>true</code> if this activation code matches the given verify event.
          * @param event the event to test for matching
          * @return whether this activation code matches <code>event</code>
          */
         public boolean matches(VerifyEvent event) {
             return (event.character == fCharacter &&
                         (fKeyCode == -1 || event.keyCode == fKeyCode) &&
                         (fStateMask == SWT.DEFAULT || event.stateMask == fStateMask));
         }
     }

     /**
      * Internal part and shell activation listener for triggering state validation.
      * @since 2.0
      */
     class ActivationListener implements IPartListener, IWindowListener {

         /** Cache of the active workbench part. */
         private IWorkbenchPart fActivePart;
         /** Indicates whether activation handling is currently be done. */
         private boolean fIsHandlingActivation= false;
         /**
          * The part service.
          * @since 3.1
          */
         private IPartService fPartService;

         /**
          * Creates this activation listener.
          *
          * @param partService the part service on which to add the part listener
          * @since 3.1
          */
         public ActivationListener(IPartService partService) {
             fPartService= partService;
             fPartService.addPartListener(this);
             PlatformUI.getWorkbench().addWindowListener(this);
         }

         /**
          * Disposes this activation listener.
          *
          * @since 3.1
          */
         public void dispose() {
             fPartService.removePartListener(this);
             PlatformUI.getWorkbench().removeWindowListener(this);
             fPartService= null;
         }

         /*
          * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
          */
         public void partActivated(IWorkbenchPart part) {
             fActivePart= part;
             handleActivation();
         }

         /*
          * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
          */
         public void partBroughtToTop(IWorkbenchPart part) {
         }

         /*
          * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
          */
         public void partClosed(IWorkbenchPart part) {
         }

         /*
          * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
          */
         public void partDeactivated(IWorkbenchPart part) {
             fActivePart= null;
         }

         /*
          * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
          */
         public void partOpened(IWorkbenchPart part) {
             // Restore the saved state if any
 if (part == AbstractTextEditor.this && fMementoToRestore != null && containsSavedState(fMementoToRestore))
                 doRestoreState(fMementoToRestore);
             fMementoToRestore= null;
         }

         /**
          * Handles the activation triggering a element state check in the editor.
          */
         private void handleActivation() {
             if (fIsHandlingActivation)
                 return;

             if (fActivePart == AbstractTextEditor.this) {
                 fIsHandlingActivation= true;
                 try {
                     safelySanityCheckState(getEditorInput());
                 } finally {
                     fIsHandlingActivation= false;
                 }
             }
         }

         /*
          * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
          * @since 3.1
          */
         public void windowActivated(IWorkbenchWindow window) {
             if (window == getEditorSite().getWorkbenchWindow()) {
                 /*
                  * Workaround for problem described in
                  * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731
                  * Will be removed when SWT has solved the problem.
                  */
                 window.getShell().getDisplay().asyncExec(new Runnable () {
                     public void run() {
                         handleActivation();
                     }
                 });
             }
         }

         /*
          * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
          * @since 3.1
          */
         public void windowDeactivated(IWorkbenchWindow window) {
         }

         /*
          * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
          * @since 3.1
          */
         public void windowClosed(IWorkbenchWindow window) {
         }

         /*
          * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
          * @since 3.1
          */
         public void windowOpened(IWorkbenchWindow window) {
         }
     }

     /**
      * Internal interface for a cursor listener. I.e. aggregation
      * of mouse and key listener.
      * @since 2.0
      */
     interface ICursorListener extends MouseListener, KeyListener {
     }

     /**
      * Maps an action definition id to an StyledText action.
      * @since 2.0
      */
     protected static final class IdMapEntry {

         /** The action id. */
         private String fActionId;
         /** The StyledText action. */
         private int fAction;

         /**
          * Creates a new mapping.
          * @param actionId the action id
          * @param action the StyledText action
          */
         public IdMapEntry(String actionId, int action) {
             fActionId= actionId;
             fAction= action;
         }

         /**
          * Returns the action id.
         * @return the action id
         */
        public String getActionId() {
            return fActionId;
        }

        /**
         * Returns the action.
         * @return the action
         */
        public int getAction() {
            return fAction;
        }
    }

    /**
     * Internal action to scroll the editor's viewer by a specified number of lines.
     * @since 2.0
     */
    class ScrollLinesAction extends Action {

        /** Number of lines to scroll. */
        private int fScrollIncrement;

        /**
         * Creates a new scroll action that scroll the given number of lines. If the
         * increment is &lt; 0, it's scrolling up, if &gt; 0 it's scrolling down.
         * @param scrollIncrement the number of lines to scroll
         */
        public ScrollLinesAction(int scrollIncrement) {
            fScrollIncrement= scrollIncrement;
        }

        /*
         * @see IAction#run()
         */
        public void run() {
            if (fSourceViewer instanceof ITextViewerExtension5) {
                ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
                StyledText textWidget= fSourceViewer.getTextWidget();
                int topIndex= textWidget.getTopIndex();
                int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
                fSourceViewer.setTopIndex(extension.widgetLine2ModelLine(newTopIndex));
            } else {
                int topIndex= fSourceViewer.getTopIndex();
                int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
                fSourceViewer.setTopIndex(newTopIndex);
            }
        }
    }

    /**
     * Action to toggle the insert mode. The action is checked if smart mode is
     * turned on.
     *
     * @since 2.1
     */
    class ToggleInsertModeAction extends ResourceAction {

        public ToggleInsertModeAction(ResourceBundle bundle, String prefix) {
            super(bundle, prefix, IAction.AS_CHECK_BOX);
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            switchToNextInsertMode();
        }

        /*
         * @see org.eclipse.jface.action.IAction#isChecked()
         * @since 3.0
         */
        public boolean isChecked() {
            return fInsertMode == SMART_INSERT;
        }
    }

    /**
     * Action to toggle the overwrite mode.
     *
     * @since 3.0
     */
    class ToggleOverwriteModeAction extends ResourceAction {

        public ToggleOverwriteModeAction(ResourceBundle bundle, String prefix) {
            super(bundle, prefix);
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            toggleOverwriteMode();
        }
    }

    /**
     * This action implements smart end.
     * Instead of going to the end of a line it does the following:
     * - if smart home/end is enabled and the caret is before the line's last non-whitespace and then the caret is moved directly after it
     * - if the caret is after last non-whitespace the caret is moved at the end of the line
     * - if the caret is at the end of the line the caret is moved directly after the line's last non-whitespace character
     * @since 2.1 (in 3.3 the access modifier changed from package visibility to protected)
     */
    protected class LineEndAction extends TextNavigationAction {

        /** boolean flag which tells if the text up to the line end should be selected. */
        private boolean fDoSelect;

        /**
         * Create a new line end action.
         *
         * @param textWidget the styled text widget
         * @param doSelect a boolean flag which tells if the text up to the line end should be selected
         */
        public LineEndAction(StyledText textWidget, boolean doSelect) {
            super(textWidget, ST.LINE_END);
            fDoSelect= doSelect;
        }

        /**
         * Computes the offset of the line end position.
         * <p>
         * XXX: will become protected in 3.4.
         * </p>
         *
         * @param document the document where to compute the line end position
         * @param line the line to determine the end position of
         * @param length the length of the line
         * @param offset the caret position in the document
         * @return the offset of the line end
         * @since 3.3
         */
        int getLineEndPosition(final IDocument document, final String line, final int length, final int offset) {
            int index= length - 1;
            while (index > -1 && Character.isWhitespace(line.charAt(index)))
                index--;
            index++;
            
            LinkedModeModel model= LinkedModeModel.getModel(document, offset);
            if (model != null) {
                LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
                if (linkedPosition != null) {
                    int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
                    int lineOffset;
                    try {
                        lineOffset= document.getLineInformationOfOffset(offset).getOffset();
                        if (offset != linkedPositionEnd && linkedPositionEnd - lineOffset < index)
                            index= linkedPositionEnd - lineOffset;
                    } catch (BadLocationException e) {
                        //should not happen
 }
                }
            }
            return index;
        }
        
        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            boolean isSmartHomeEndEnabled= false;
            IPreferenceStore store= getPreferenceStore();
            if (store != null)
                isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);

            StyledText st= fSourceViewer.getTextWidget();
            if (st == null || st.isDisposed())
                return;
            int caretOffset= st.getCaretOffset();
            int lineNumber= st.getLineAtOffset(caretOffset);
            int lineOffset= st.getOffsetAtLine(lineNumber);

            int lineLength;
            int caretOffsetInDocument;
            final IDocument document= fSourceViewer.getDocument();

            try {
                caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
                lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
            } catch (BadLocationException ex) {
                return;
            }
            int lineEndOffset= lineOffset + lineLength;

            int delta= lineEndOffset - st.getCharCount();
            if (delta > 0) {
                lineEndOffset -= delta;
                lineLength -= delta;
            }

            String line= ""; //$NON-NLS-1$
 if (lineLength > 0)
                line= st.getText(lineOffset, lineEndOffset - 1);

            // Remember current selection
 Point oldSelection= st.getSelection();

            // The new caret position
 int newCaretOffset= -1;

            if (isSmartHomeEndEnabled) {
                // Compute the line end offset
 int i= getLineEndPosition(document, line, lineLength, caretOffsetInDocument);

                if (caretOffset - lineOffset == i)
                    // to end of line
 newCaretOffset= lineEndOffset;
                else
                    // to end of text
 newCaretOffset= lineOffset + i;

            } else {

                if (caretOffset < lineEndOffset)
                    // to end of line
 newCaretOffset= lineEndOffset;

            }

            if (newCaretOffset == -1)
                newCaretOffset= caretOffset;
            else
                st.setCaretOffset(newCaretOffset);

            st.setCaretOffset(newCaretOffset);
            if (fDoSelect) {
                if (caretOffset < oldSelection.y)
                    st.setSelection(oldSelection.y, newCaretOffset);
                else
                    st.setSelection(oldSelection.x, newCaretOffset);
            } else
                st.setSelection(newCaretOffset);

            fireSelectionChanged(oldSelection);
        }
    }

    /**
     * This action implements smart home.
     * Instead of going to the start of a line it does the following:
     * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it
     * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
     * - if the caret is at the beginning of the line the caret is moved directly before the line's first non-whitespace character
     * @since 2.1
     */
    protected class LineStartAction extends TextNavigationAction {

        /** boolean flag which tells if the text up to the beginning of the line should be selected. */
        private final boolean fDoSelect;

        /**
         * Creates a new line start action.
         *
         * @param textWidget the styled text widget
         * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
         */
        public LineStartAction(final StyledText textWidget, final boolean doSelect) {
            super(textWidget, ST.LINE_START);
            fDoSelect= doSelect;
        }

        /**
         * Computes the offset of the line start position.
         *
         * @param document the document where to compute the line start position
         * @param line the line to determine the start position of
         * @param length the length of the line
         * @param offset the caret position in the document
         * @return the offset of the line start
         * @since 3.0
         */
        protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) {
            int index= 0;
            while (index < length && Character.isWhitespace(line.charAt(index)))
                index++;
            
            LinkedModeModel model= LinkedModeModel.getModel(document, offset);
            if (model != null) {
                LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
                if (linkedPosition != null) {
                    int linkedPositionOffset= linkedPosition.getOffset();
                    int lineOffset;
                    try {
                        lineOffset= document.getLineInformationOfOffset(offset).getOffset();
                        if (offset != linkedPositionOffset && index < linkedPositionOffset - lineOffset)
                            index= linkedPositionOffset - lineOffset;
                    } catch (BadLocationException e) {
                        //should not happen
 }
                }
            }
            return index;
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            boolean isSmartHomeEndEnabled= false;
            IPreferenceStore store= getPreferenceStore();
            if (store != null)
                isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);

            StyledText st= fSourceViewer.getTextWidget();
            if (st == null || st.isDisposed())
                return;

            int caretOffset= st.getCaretOffset();
            int lineNumber= st.getLineAtOffset(caretOffset);
            int lineOffset= st.getOffsetAtLine(lineNumber);

            int lineLength;
            int caretOffsetInDocument;
            final IDocument document= fSourceViewer.getDocument();

            try {
                caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
                lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
            } catch (BadLocationException ex) {
                return;
            }

            String line= ""; //$NON-NLS-1$
 if (lineLength > 0) {
                int end= lineOffset + lineLength - 1;
                end= Math.min(end, st.getCharCount() -1);
                line= st.getText(lineOffset, end);
            }

            // Remember current selection
 Point oldSelection= st.getSelection();

            // The new caret position
 int newCaretOffset= -1;
            
            if (isSmartHomeEndEnabled) {
                
                // Compute the line start offset
 int index= getLineStartPosition(document, line, lineLength, caretOffsetInDocument);

                if (caretOffset - lineOffset == index)
                    // to beginning of line
 newCaretOffset= lineOffset;
                else
                    // to beginning of text
 newCaretOffset= lineOffset + index;

            } else {

                if (caretOffset > lineOffset)
                    // to beginning of line
 newCaretOffset= lineOffset;
            }

            if (newCaretOffset == -1)
                newCaretOffset= caretOffset;
            else
                st.setCaretOffset(newCaretOffset);

            if (fDoSelect) {
                if (caretOffset < oldSelection.y)
                    st.setSelection(oldSelection.y, newCaretOffset);
                else
                    st.setSelection(oldSelection.x, newCaretOffset);
            } else
                st.setSelection(newCaretOffset);

            fireSelectionChanged(oldSelection);
        }

    }

    /**
     * Internal action to show the editor's ruler context menu (accessibility).
     * @since 2.0
     */
    class ShowRulerContextMenuAction extends Action {
        /*
         * @see IAction#run()
         */
        public void run() {
            if (fSourceViewer == null)
                return;

            StyledText text= fSourceViewer.getTextWidget();
            if (text == null || text.isDisposed())
                return;

            Point location= text.getLocationAtOffset(text.getCaretOffset());
            location.x= 0;

            if (fVerticalRuler instanceof IVerticalRulerExtension)
                ((IVerticalRulerExtension) fVerticalRuler).setLocationOfLastMouseButtonActivity(location.x, location.y);

            location= text.toDisplay(location);
            fRulerContextMenu.setLocation(location.x, location.y);
            fRulerContextMenu.setVisible(true);
        }
    }


    /**
     * Editor specific selection provider which wraps the source viewer's selection provider.
     * @since 2.1
     */
    class SelectionProvider implements IPostSelectionProvider, ISelectionValidator {

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
         */
        public void addSelectionChangedListener(ISelectionChangedListener listener) {
            if (fSourceViewer != null)
                fSourceViewer.getSelectionProvider().addSelectionChangedListener(listener);
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
         */
        public ISelection getSelection() {
            return doGetSelection();
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
         */
        public void removeSelectionChangedListener(ISelectionChangedListener listener) {
            if (fSourceViewer != null)
                fSourceViewer.getSelectionProvider().removeSelectionChangedListener(listener);
        }

        /*
         * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection)
         */
        public void setSelection(ISelection selection) {
            doSetSelection(selection);
        }

        /*
         * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
         * @since 3.0
         */
        public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
            if (fSourceViewer != null) {
                if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
                    IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
                    provider.addPostSelectionChangedListener(listener);
                }
            }
        }

        /*
         * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
         * @since 3.0
         */
        public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
            if (fSourceViewer != null) {
                if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
                    IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
                    provider.removePostSelectionChangedListener(listener);
                }
            }
        }

        /*
         * @see org.eclipse.jface.text.IPostSelectionValidator#isValid()
         * @since 3.0
         */
        public boolean isValid(ISelection postSelection) {
            return fSelectionListener != null && fSelectionListener.isValid(postSelection);
    }
    }

    /**
     * Internal implementation class for a change listener.
     * @since 3.0
     */
    protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener {

        /**
         * Installs this selection changed listener with the given selection provider. If
         * the selection provider is a post selection provider, post selection changed
         * events are the preferred choice, otherwise normal selection changed events
         * are requested.
         *
         * @param selectionProvider
         */
        public void install(ISelectionProvider selectionProvider) {
            if (selectionProvider == null)
                return;

            if (selectionProvider instanceof IPostSelectionProvider) {
                IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
                provider.addPostSelectionChangedListener(this);
            } else {
                selectionProvider.addSelectionChangedListener(this);
            }
        }

        /**
         * Removes this selection changed listener from the given selection provider.
         *
         * @param selectionProvider the selection provider
         */
        public void uninstall(ISelectionProvider selectionProvider) {
            if (selectionProvider == null)
                return;

            if (selectionProvider instanceof IPostSelectionProvider) {
                IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
                provider.removePostSelectionChangedListener(this);
            } else {
                selectionProvider.removeSelectionChangedListener(this);
            }
        }
    }

    /**
     * This selection listener allows the SelectionProvider to implement {@link ISelectionValidator}.
     *
     * @since 3.0
     */
    private class SelectionListener extends AbstractSelectionChangedListener implements IDocumentListener {

        private IDocument fDocument;
        private final Object INVALID_SELECTION= new Object ();
        private Object fPostSelection= INVALID_SELECTION;

        /*
         * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
         */
        public synchronized void selectionChanged(SelectionChangedEvent event) {
            fPostSelection= event.getSelection();
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
         * @since 3.0
         */
        public synchronized void documentAboutToBeChanged(DocumentEvent event) {
            fPostSelection= INVALID_SELECTION;
        }

        /*
         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
         * @since 3.0
         */
        public void documentChanged(DocumentEvent event) {
        }

        public synchronized boolean isValid(ISelection selection) {
            return fPostSelection != INVALID_SELECTION && fPostSelection == selection;
        }

        public void setDocument(IDocument document) {
            if (fDocument != null)
                fDocument.removeDocumentListener(this);

            fDocument= document;
            if (fDocument != null)
                fDocument.addDocumentListener(this);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#install(org.eclipse.jface.viewers.ISelectionProvider)
         * @since 3.0
         */
        public void install(ISelectionProvider selectionProvider) {
            super.install(selectionProvider);

            if (selectionProvider != null)
                selectionProvider.addSelectionChangedListener(this);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#uninstall(org.eclipse.jface.viewers.ISelectionProvider)
         * @since 3.0
         */
        public void uninstall(ISelectionProvider selectionProvider) {
            if (selectionProvider != null)
                selectionProvider.removeSelectionChangedListener(this);

            if (fDocument != null) {
                fDocument.removeDocumentListener(this);
                fDocument= null;
            }
            super.uninstall(selectionProvider);
        }
    }
    
    
    /**
     * Implements the ruler column support of for the given editor.
     * <p>
     * This is currently only used to support vertical ruler columns.
     * </p>
     *
     * @since 3.3
     */
    protected static class ColumnSupport implements IColumnSupport {
        private final AbstractTextEditor fEditor;
        private final RulerColumnRegistry fRegistry;
        private final List fColumns;

        /**
         * Creates a new column support for the given editor. Only the editor itself should normally
         * create such an instance.
         *
         * @param editor the editor
         * @param registry the contribution registry to refer to
         */
        public ColumnSupport(AbstractTextEditor editor, RulerColumnRegistry registry) {
            Assert.isLegal(editor != null);
            Assert.isLegal(registry != null);
            fEditor= editor;
            fRegistry= registry;
            fColumns= new ArrayList ();
        }

        /*
         * @see org.eclipse.ui.texteditor.IColumnSupport#setColumnVisible(java.lang.String, boolean)
         */
        public final void setColumnVisible(RulerColumnDescriptor descriptor, boolean visible) {
            Assert.isLegal(descriptor != null);

            final CompositeRuler ruler= getRuler();
            if (ruler == null)
                return;

            if (!isColumnSupported(descriptor))
                visible= false;

            if (isColumnVisible(descriptor)) {
                if (!visible)
                    removeColumn(ruler, descriptor);
            } else {
                if (visible)
                    addColumn(ruler, descriptor);
            }
        }
        
        private void addColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
            
            final int idx= computeIndex(ruler, descriptor);
            
            SafeRunnable runnable= new SafeRunnable() {
                public void run() throws Exception {
                    IContributedRulerColumn column= descriptor.createColumn(fEditor);
                    fColumns.add(column);
                    initializeColumn(column);
                    ruler.addDecorator(idx, column);
                }
            };
            SafeRunner.run(runnable);
        }

        /**
         * Hook to let subclasses initialize a newly created column.
         * <p>
         * Subclasses may extend this method.</p>
         *
         * @param column the created column
         */
        protected void initializeColumn(IContributedRulerColumn column) {
        }

        private void removeColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
            removeColumn(ruler, getVisibleColumn(ruler, descriptor));
        }
        
        private void removeColumn(final CompositeRuler ruler, final IContributedRulerColumn rulerColumn) {
            if (rulerColumn != null) {
                SafeRunnable runnable= new SafeRunnable() {
                    public void run() throws Exception {
                        if (ruler != null)
                            ruler.removeDecorator(rulerColumn);
                        rulerColumn.columnRemoved();
                    }
                };
                SafeRunner.run(runnable);
            }
        }

        /**
         * Returns the currently visible column matching <code>id</code>, <code>null</code> if
         * none.
         *
         * @param ruler the composite ruler to scan
         * @param descriptor the descriptor of the column of interest
         * @return the matching column or <code>null</code>
         */
        private IContributedRulerColumn getVisibleColumn(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
            for (Iterator it= ruler.getDecoratorIterator(); it.hasNext();) {
                IVerticalRulerColumn column= (IVerticalRulerColumn)it.next();
                if (column instanceof IContributedRulerColumn) {
                    IContributedRulerColumn rulerColumn= (IContributedRulerColumn)column;
                    RulerColumnDescriptor rcd= rulerColumn.getDescriptor();
                    if (descriptor.equals(rcd))
                        return rulerColumn;
                }
            }
            return null;
        }

        /**
         * Computes the insertion index for a column contribution into the currently visible columns.
         *
         * @param ruler the composite ruler into which to insert the column
         * @param descriptor the descriptor to compute the index for
         * @return the insertion index for a new column
         */
        private int computeIndex(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
            int index= 0;
            List all= fRegistry.getColumnDescriptors();
            int newPos= all.indexOf(descriptor);
            for (Iterator it= ruler.getDecoratorIterator(); it.hasNext();) {
                IVerticalRulerColumn column= (IVerticalRulerColumn) it.next();
                if (column instanceof IContributedRulerColumn) {
                    RulerColumnDescriptor rcd= ((IContributedRulerColumn)column).getDescriptor();
                    if (rcd != null && all.indexOf(rcd) > newPos)
                        break;
                } else if ("org.eclipse.jface.text.source.projection.ProjectionRulerColumn".equals(column.getClass().getName())) { //$NON-NLS-1$
 // projection column is always the rightmost column
 break;
                }
                index++;
            }
            return index;
        }

        /*
         * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnVisible(java.lang.String)
         */
        public final boolean isColumnVisible(RulerColumnDescriptor descriptor) {
            Assert.isLegal(descriptor != null);
            CompositeRuler ruler= getRuler();
            return ruler != null && getVisibleColumn(ruler, descriptor) != null;
        }
        
        /*
         * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnSupported(java.lang.String)
         */
        public final boolean isColumnSupported(RulerColumnDescriptor descriptor) {
            Assert.isLegal(descriptor != null);
            if (getRuler() == null)
                return false;
            
            if (descriptor == null)
                return false;
            
            return descriptor.matchesEditor(fEditor);
        }
        
        /**
         * Returns the editor's vertical ruler, if it is a {@link CompositeRuler}, <code>null</code>
         * otherwise.
         *
         * @return the editor's {@link CompositeRuler} or <code>null</code>
         */
        private CompositeRuler getRuler() {
            Object ruler= fEditor.getAdapter(IVerticalRulerInfo.class);
            if (ruler instanceof CompositeRuler)
                return (CompositeRuler) ruler;
            return null;
        }

        /**
         * {@inheritDoc}
         * <p>
         * Subclasses may extend this method.</p>
         *
         */
        public void dispose() {
            for (Iterator iter= new ArrayList (fColumns).iterator(); iter.hasNext();)
                removeColumn(getRuler(), (IContributedRulerColumn)iter.next());
            fColumns.clear();
        }
    }



    /**
     * Information provider used to present focusable information shells.
     *
     * @since 3.3
     */
    private static final class InformationProvider implements IInformationProvider, IInformationProviderExtension, IInformationProviderExtension2 {
        
        private IRegion fHoverRegion;
        private Object fHoverInfo;
        private IInformationControlCreator fControlCreator;
        
        InformationProvider(IRegion hoverRegion, Object hoverInfo, IInformationControlCreator controlCreator) {
            fHoverRegion= hoverRegion;
            fHoverInfo= hoverInfo;
            fControlCreator= controlCreator;
        }
        /*
         * @see org.eclipse.jface.text.information.IInformationProvider#getSubject(org.eclipse.jface.text.ITextViewer, int)
         */
        public IRegion getSubject(ITextViewer textViewer, int invocationOffset) {
            return fHoverRegion;
        }
        /*
         * @see org.eclipse.jface.text.information.IInformationProvider#getInformation(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
         */
        public String getInformation(ITextViewer textViewer, IRegion subject) {
            return fHoverInfo.toString();
        }
        /*
         * @see org.eclipse.jface.text.information.IInformationProviderExtension#getInformation2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
         * @since 3.2
         */
        public Object getInformation2(ITextViewer textViewer, IRegion subject) {
            return fHoverInfo;
        }
        /*
         * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
         */
        public IInformationControlCreator getInformationPresenterControlCreator() {
            return fControlCreator;
        }
    }
    
    /**
     * This action behaves in two different ways: If there is no current text
     * hover, the javadoc is displayed using information presenter. If there is
     * a current text hover, it is converted into a information presenter in
     * order to make it sticky.
     *
     * @since 3.3
     */
    private final class InformationDispatchAction extends TextEditorAction {

        /** The wrapped text operation action. */
        private final TextOperationAction fTextOperationAction;

        /**
         * Creates a dispatch action.
         *
         * @param resourceBundle the resource bundle
         * @param prefix the prefix
         * @param textOperationAction the text operation action
         */
        public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) {
            super(resourceBundle, prefix, AbstractTextEditor.this);
            if (textOperationAction == null)
                throw new IllegalArgumentException ();
            fTextOperationAction= textOperationAction;
        }

        /*
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {

            ISourceViewer sourceViewer= getSourceViewer();
            if (sourceViewer == null) {
                if (fTextOperationAction.isEnabled())
                    fTextOperationAction.run();
                return;
            }

            if (sourceViewer instanceof ITextViewerExtension4) {
                ITextViewerExtension4 extension4= (ITextViewerExtension4) sourceViewer;
                if (extension4.moveFocusToWidgetToken())
                    return;
            }

            if (sourceViewer instanceof ITextViewerExtension2) {
                // does a text hover exist?
 ITextHover textHover= ((ITextViewerExtension2) sourceViewer).getCurrentTextHover();
                if (textHover != null && makeTextHoverFocusable(sourceViewer, textHover))
                    return;
            }

            if (sourceViewer instanceof ISourceViewerExtension3) {
                // does an annotation hover exist?
 IAnnotationHover annotationHover= ((ISourceViewerExtension3) sourceViewer).getCurrentAnnotationHover();
                if (annotationHover != null && makeAnnotationHoverFocusable(sourceViewer, annotationHover))
                    return;
            }
            
            // otherwise, just run the action
 if (fTextOperationAction.isEnabled())
                fTextOperationAction.run();
        }

        /**
         * Tries to make a text hover focusable (or "sticky").
         *
         * @param sourceViewer the source viewer to display the hover over
         * @param textHover the hover to make focusable
         * @return <code>true</code> if successful, <code>false</code> otherwise
         */
        private boolean makeTextHoverFocusable(ISourceViewer sourceViewer, ITextHover textHover) {
            Point hoverEventLocation= ((ITextViewerExtension2) sourceViewer).getHoverEventLocation();
            int offset= computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
            if (offset == -1)
                return false;
            
            try {
                IRegion hoverRegion= textHover.getHoverRegion(sourceViewer, offset);
                if (hoverRegion == null)
                    return false;

                String hoverInfo= textHover.getHoverInfo(sourceViewer, hoverRegion);

                IInformationControlCreator controlCreator= null;
                if (textHover instanceof IInformationProviderExtension2)
                    controlCreator= ((IInformationProviderExtension2)textHover).getInformationPresenterControlCreator();

                IInformationProvider informationProvider= new InformationProvider(hoverRegion, hoverInfo, controlCreator);

                fInformationPresenter.setOffset(offset);
                fInformationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM);
                fInformationPresenter.setMargins(6, 6); // default values from AbstractInformationControlManager
 String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()), offset, true);
                fInformationPresenter.setInformationProvider(informationProvider, contentType);
                fInformationPresenter.showInformation();

                return true;

            } catch (BadLocationException e) {
                return false;
            }
        }

        /**
         * Tries to make an annotation hover focusable (or "sticky").
         *
         * @param sourceViewer the source viewer to display the hover over
         * @param annotationHover the hover to make focusable
         * @return <code>true</code> if successful, <code>false</code> otherwise
         */
        private boolean makeAnnotationHoverFocusable(ISourceViewer sourceViewer, IAnnotationHover annotationHover) {
            IVerticalRulerInfo info= getVerticalRuler();
            int line= info.getLineOfLastMouseButtonActivity();
            if (line == -1)
                return false;

            try {

                // compute the hover information
 Object hoverInfo;
                if (annotationHover instanceof IAnnotationHoverExtension) {
                    IAnnotationHoverExtension extension= (IAnnotationHoverExtension) annotationHover;
                    ILineRange hoverLineRange= extension.getHoverLineRange(sourceViewer, line);
                    if (hoverLineRange == null)
                        return false;
                    final int maxVisibleLines= Integer.MAX_VALUE; // allow any number of lines being displayed, as we support scrolling
 hoverInfo= extension.getHoverInfo(sourceViewer, hoverLineRange, maxVisibleLines);
                } else {
                    hoverInfo= annotationHover.getHoverInfo(sourceViewer, line);
                }
                
                // hover region: the beginning of the concerned line to place the control right over the line
 IDocument document= sourceViewer.getDocument();
                int offset= document.getLineOffset(line);
                String contentType= TextUtilities.getContentType(document, getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()), offset, true);

                IInformationControlCreator controlCreator= null;
                if (annotationHover instanceof IInformationProviderExtension2)
                    controlCreator= ((IInformationProviderExtension2) annotationHover).getInformationPresenterControlCreator();
                else if (annotationHover instanceof IAnnotationHoverExtension)
                    controlCreator= ((IAnnotationHoverExtension) annotationHover).getHoverControlCreator();
                
                IInformationProvider informationProvider= new InformationProvider(new Region(offset, 0), hoverInfo, controlCreator);

                fInformationPresenter.setOffset(offset);
                fInformationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_RIGHT);
                fInformationPresenter.setMargins(4, 0); // AnnotationBarHoverManager sets (5,0), minus SourceViewer.GAP_SIZE_1
 fInformationPresenter.setInformationProvider(informationProvider, contentType);
                fInformationPresenter.showInformation();

                return true;

            } catch (BadLocationException e) {
                return false;
            }
        }

        // modified version from TextViewer
 private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {

            StyledText styledText= textViewer.getTextWidget();
            IDocument document= textViewer.getDocument();

            if (document == null)
                return -1;

            try {
                int widgetOffset= styledText.getOffsetAtLocation(new Point(x, y));
                Point p= styledText.getLocationAtOffset(widgetOffset);
                if (p.x > x)
                    widgetOffset--;
                
                if (textViewer instanceof ITextViewerExtension5) {
                    ITextViewerExtension5 extension= (ITextViewerExtension5) textViewer;
                    return extension.widgetOffset2ModelOffset(widgetOffset);
                }
                IRegion visibleRegion= textViewer.getVisibleRegion();
                return widgetOffset + visibleRegion.getOffset();
            } catch (IllegalArgumentException e) {
                return -1;
            }

        }
    }
    
    
    /**
     * Key used to look up font preference.
     * Value: <code>"org.eclipse.jface.textfont"</code>
     *
     * @deprecated As of 2.1, replaced by {@link JFaceResources#TEXT_FONT}
     */
    public final static String PREFERENCE_FONT= JFaceResources.TEXT_FONT;
    /**
     * Key used to look up foreground color preference.
     * Value: <code>AbstractTextEditor.Color.Foreground</code>
     * @since 2.0
     */
    public final static String PREFERENCE_COLOR_FOREGROUND= "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$
 /**
     * Key used to look up background color preference.
     * Value: <code>AbstractTextEditor.Color.Background</code>
     * @since 2.0
     */
    public final static String PREFERENCE_COLOR_BACKGROUND= "AbstractTextEditor.Color.Background"; //$NON-NLS-1$
 /**
     * Key used to look up foreground color system default preference.
     * Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code>
     * @since 2.0
     */
    public final static String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$
 /**
     * Key used to look up background color system default preference.
     * Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code>
     * @since 2.0
     */
    public final static String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$
 /**
     * Key used to look up selection foreground color preference.
     * Value: <code>AbstractTextEditor.Color.SelectionForeground</code>
     * @since 3.0
     */
    public final static String PREFERENCE_COLOR_SELECTION_FOREGROUND= "AbstractTextEditor.Color.SelectionForeground"; //$NON-NLS-1$
 /**
     * Key used to look up selection background color preference.
     * Value: <code>AbstractTextEditor.Color.SelectionBackground</code>
     * @since 3.0
     */
    public final static String PREFERENCE_COLOR_SELECTION_BACKGROUND= "AbstractTextEditor.Color.SelectionBackground"; //$NON-NLS-1$
 /**
     * Key used to look up selection foreground color system default preference.
     * Value: <code>AbstractTextEditor.Color.SelectionForeground.SystemDefault</code>
     * @since 3.0
     */
    public final static String PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionForeground.SystemDefault"; //$NON-NLS-1$
 /**
     * Key used to look up selection background color system default preference.
     * Value: <code>AbstractTextEditor.Color.SelectionBackground.SystemDefault</code>
     * @since 3.0
     */
    public final static String PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionBackground.SystemDefault"; //$NON-NLS-1$
 /**
     * Key used to look up find scope background color preference.
     * Value: <code>AbstractTextEditor.Color.FindScope</code>
     * @since 2.0
     */
    public final static String PREFERENCE_COLOR_FIND_SCOPE= "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$
 /**
     * Key used to look up smart home/end preference.
     * Value: <code>AbstractTextEditor.Navigation.SmartHomeEnd</code>
     * @since 2.1
     */
    public final static String PREFERENCE_NAVIGATION_SMART_HOME_END= "AbstractTextEditor.Navigation.SmartHomeEnd"; //$NON-NLS-1$
 /**
     * Key used to look up the custom caret preference.
     * Value: {@value}
     * @since 3.0
     */
    public final static String PREFERENCE_USE_CUSTOM_CARETS= "AbstractTextEditor.Accessibility.UseCustomCarets"; //$NON-NLS-1$
 /**
     * Key used to look up the caret width preference.
     * Value: {@value}
     * @since 3.0
     */
    public final static String PREFERENCE_WIDE_CARET= "AbstractTextEditor.Accessibility.WideCaret"; //$NON-NLS-1$
 /**
     * A named preference that controls if hyperlinks are turned on or off.
     * <p>
     * Value is of type <code>Boolean</code>.
     * </p>
     *
     * @since 3.1
     */
    public static final String PREFERENCE_HYPERLINKS_ENABLED= "hyperlinksEnabled"; //$NON-NLS-1$

    /**
     * A named preference that controls the key modifier for hyperlinks.
     * <p>
     * Value is of type <code>String</code>.
     * </p>
     *
     * @since 3.1
     */
    public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER= "hyperlinkKeyModifier"; //$NON-NLS-1$
 /**
     * A named preference that controls the key modifier mask for hyperlinks.
     * The value is only used if the value of <code>PREFERENCE_HYPERLINK_KEY_MODIFIER</code>
     * cannot be resolved to valid SWT modifier bits.
     * <p>
     * Value is of type <code>String</code>.
     * </p>
     *
     * @see #PREFERENCE_HYPERLINK_KEY_MODIFIER
     * @since 3.1
     */
    public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER_MASK= "hyperlinkKeyModifierMask"; //$NON-NLS-1$
 /**
     * A named preference that controls the visible ruler column contributions.
     * <p>
     * Value is of type <code>String</code> and should be read using a {@link RulerColumnPreferenceAdapter}.
     * </p>
     *
     * @since 3.3
     */
    public static final String PREFERENCE_RULER_CONTRIBUTIONS= "rulerContributions"; //$NON-NLS-1$
 /**
     * A named preference that controls the display of whitespace characters.
     * <p>
     * Value is of type <code>Boolean</code>.
     * </p>
     *
     * @since 3.3
     */
    public static final String PREFERENCE_SHOW_WHITESPACE_CHARACTERS= "showWhitespaceCharacters"; //$NON-NLS-1$
 /**
     * A named preference that controls whether text drag and drop is enabled.
     * <p>
     * Value is of type <code>Boolean</code>.
     * </p>
     *
     * @since 3.3
     */
    public static final String PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED= "textDragAndDropEnabled"; //$NON-NLS-1$


    /** Menu id for the editor context menu. */
    public final static String DEFAULT_EDITOR_CONTEXT_MENU_ID= "#EditorContext"; //$NON-NLS-1$
 /** Menu id for the ruler context menu. */
    public final static String DEFAULT_RULER_CONTEXT_MENU_ID= "#RulerContext"; //$NON-NLS-1$

    /** The width of the vertical ruler. */
    protected final static int VERTICAL_RULER_WIDTH= 12;

    /**
     * The complete mapping between action definition IDs used by eclipse and StyledText actions.
     *
     * @since 2.0
     */
    protected final static IdMapEntry[] ACTION_MAP= new IdMapEntry[] {
        // navigation
 new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP, ST.LINE_UP),
        new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN, ST.LINE_DOWN),
        new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START, ST.LINE_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END, ST.LINE_END),
        new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_PREVIOUS, ST.COLUMN_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT, ST.COLUMN_NEXT),
        new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP, ST.PAGE_UP),
        new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN, ST.PAGE_DOWN),
        new IdMapEntry(ITextEditorActionDefinitionIds.WORD_PREVIOUS, ST.WORD_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT, ST.WORD_NEXT),
        new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START, ST.TEXT_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END, ST.TEXT_END),
        new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START, ST.WINDOW_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END, ST.WINDOW_END),
        // selection
 new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_UP, ST.SELECT_LINE_UP),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_DOWN, ST.SELECT_LINE_DOWN),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_START, ST.SELECT_LINE_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_END, ST.SELECT_LINE_END),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS, ST.SELECT_COLUMN_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT, ST.SELECT_COLUMN_NEXT),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_UP, ST.SELECT_PAGE_UP),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN, ST.SELECT_PAGE_DOWN),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, ST.SELECT_WORD_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, ST.SELECT_WORD_NEXT),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_START, ST.SELECT_TEXT_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_END, ST.SELECT_TEXT_END),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_START, ST.SELECT_WINDOW_START),
        new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_END, ST.SELECT_WINDOW_END),
        // modification
 new IdMapEntry(IWorkbenchActionDefinitionIds.CUT, ST.CUT),
        new IdMapEntry(IWorkbenchActionDefinitionIds.COPY, ST.COPY),
        new IdMapEntry(IWorkbenchActionDefinitionIds.PASTE, ST.PASTE),
        new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS, ST.DELETE_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT, ST.DELETE_NEXT),
        new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, ST.DELETE_WORD_PREVIOUS),
        new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, ST.DELETE_WORD_NEXT),
        // miscellaneous
 new IdMapEntry(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, ST.TOGGLE_OVERWRITE)
    };

    private final String fReadOnlyLabel= EditorMessages.Editor_statusline_state_readonly_label;
    private final String fWritableLabel= EditorMessages.Editor_statusline_state_writable_label;
    private final String fInsertModeLabel= EditorMessages.Editor_statusline_mode_insert_label;
    private final String fOverwriteModeLabel= EditorMessages.Editor_statusline_mode_overwrite_label;
    private final String fSmartInsertModeLabel= EditorMessages.Editor_statusline_mode_smartinsert_label;

    /** The error message shown in the status line in case of failed information look up. */
    protected final String fErrorLabel= EditorMessages.Editor_statusline_error_label;

    /**
     * Data structure for the position label value.
     */
    private static class PositionLabelValue {

        public int fValue;

        public String toString() {
            return String.valueOf(fValue);
        }
    }
    /** The pattern used to show the position label in the status line. */
    private final String fPositionLabelPattern= EditorMessages.Editor_statusline_position_pattern;
    /** The position label value of the current line. */
    private final PositionLabelValue fLineLabel= new PositionLabelValue();
    /** The position label value of the current column. */
    private final PositionLabelValue fColumnLabel= new PositionLabelValue();
    /** The arguments for the position label pattern. */
    private final Object [] fPositionLabelPatternArguments= new Object [] { fLineLabel, fColumnLabel };
    /**
     * The column support of this editor.
     * @since 3.3
     */
    private IColumnSupport fColumnSupport;

    /** The editor's explicit document provider. */
    private IDocumentProvider fExplicitDocumentProvider;
    /** The editor's preference store. */
    private IPreferenceStore fPreferenceStore;
    /** The editor's range indicator. */
    private Annotation fRangeIndicator;
    /** The editor's source viewer configuration. */
    private SourceViewerConfiguration fConfiguration;
    /** The editor's source viewer. */
    private ISourceViewer fSourceViewer;
    /**
     * The editor's selection provider.
     * @since 2.1
     */
    private SelectionProvider fSelectionProvider= new SelectionProvider();
    /**
     * The editor's selection listener.
     * @since 3.0
     */
    private SelectionListener fSelectionListener;
    /** The editor's font. */
    private Font fFont; /**
     * The editor's foreground color.
     * @since 2.0
     */
    private Color fForegroundColor;
    /**
     * The editor's background color.
     * @since 2.0
     */
    private Color fBackgroundColor;
    /**
     * The editor's selection foreground color.
     * @since 3.0
     */
    private Color fSelectionForegroundColor;
    /**
     * The editor's selection background color.
     * @since 3.0
     */
    private Color fSelectionBackgroundColor;
    /**
     * The find scope's highlight color.
     * @since 2.0
     */
    private Color fFindScopeHighlightColor;

    /**
     * The editor's status line.
     * @since 2.1
     */
    private IEditorStatusLine fEditorStatusLine;
    /** The editor's vertical ruler. */
    private IVerticalRuler fVerticalRuler;
    /** The editor's context menu id. */
    private String fEditorContextMenuId;
    /** The ruler's context menu id. */
    private String fRulerContextMenuId;
    /** The editor's help context id. */
    private String fHelpContextId;
    /** The editor's presentation mode. */
    private boolean fShowHighlightRangeOnly;
    /** The actions registered with the editor. */
    private Map fActions= new HashMap (10);
    /** The actions marked as selection dependent. */
    private List fSelectionActions= new ArrayList (5);
    /** The actions marked as content dependent. */
    private List fContentActions= new ArrayList (5);
    /**
     * The actions marked as property dependent.
     * @since 2.0
     */
    private List fPropertyActions= new ArrayList (5);
    /**
     * The actions marked as state dependent.
     * @since 2.0
     */
    private List fStateActions= new ArrayList (5);
    /** The editor's action activation codes. */
    private List fActivationCodes= new ArrayList (2);
    /** The verify key listener for activation code triggering. */
    private ActivationCodeTrigger fActivationCodeTrigger= new ActivationCodeTrigger();
    /** Context menu listener. */
    private IMenuListener fMenuListener;
    /** Vertical ruler mouse listener. */
    private MouseListener fMouseListener;
    /** Selection changed listener. */
    private ISelectionChangedListener fSelectionChangedListener;
    /** Title image to be disposed. */
    private Image fTitleImage;
    /** The text context menu to be disposed. */
    private Menu fTextContextMenu;
    /** The ruler context menu to be disposed. */
    private Menu fRulerContextMenu;
    /** The editor's element state listener. */
    private IElementStateListener fElementStateListener= new ElementStateListener();
    /**
     * The editor's text input listener.
     * @since 2.1
     */
    private TextInputListener fTextInputListener= new TextInputListener();
    /** The editor's text listener. */
    private TextListener fTextListener= new TextListener();
    /** The editor's property change listener. */
    private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener();
    /**
     * The editor's font properties change listener.
     * @since 2.1
     */
    private IPropertyChangeListener fFontPropertyChangeListener= new FontPropertyChangeListener();

    /**
     * The editor's activation listener.
     * @since 2.0
     */
    private ActivationListener fActivationListener;
    /**
     * The map of the editor's status fields.
     * @since 2.0
     */
    private Map fStatusFields;
    /**
     * The editor's cursor listener.
     * @since 2.0
     */
    private ICursorListener fCursorListener;
    /**
     * The editor's remembered text selection.
     * @since 2.0
     */
    private ISelection fRememberedSelection;
    /**
     * Indicates whether the editor runs in 1.0 context menu registration compatibility mode.
     * @since 2.0
     */
    private boolean fCompatibilityMode= true;
    /**
     * The number of re-entrances into error correction code while saving.
     * @since 2.0
     */
    private int fErrorCorrectionOnSave;
    /**
     * The delete line target.
     * @since 2.1
     */
    private DeleteLineTarget fDeleteLineTarget;
    /**
     * The incremental find target.
     * @since 2.0
     */
    private IncrementalFindTarget fIncrementalFindTarget;
    /**
     * The mark region target.
     * @since 2.0
     */
    private IMarkRegionTarget fMarkRegionTarget;
    /**
     * Cached modification stamp of the editor's input.
     * @since 2.0
     */
    private long fModificationStamp= -1;
    /**
     * Ruler context menu listeners.
     * @since 2.0
     */
    private List fRulerContextMenuListeners= new ArrayList ();
    /**
     * Indicates whether sanity checking in enabled.
     * @since 2.0
     */
    private boolean fIsSanityCheckEnabled= true;
    /**
     * The find replace target.
     * @since 2.1
     */
    private FindReplaceTarget fFindReplaceTarget;
    /**
     * Indicates whether state validation is enabled.
     * @since 2.1
     */
    private boolean fIsStateValidationEnabled= true;
    /**
     * The key binding scopes of this editor.
     * @since 2.1
     */
    private String [] fKeyBindingScopes;
    /**
     * Whether the overwrite mode can be turned on.
     * @since 3.0
     */
    private boolean fIsOverwriteModeEnabled= true;
    /**
     * Whether the overwrite mode is currently on.
     * @since 3.0
     */
    private boolean fIsOverwriting= false;
    /**
     * The editor's insert mode.
     * @since 3.0
     */
    private InsertMode fInsertMode= SMART_INSERT;
    /**
     * The sequence of legal editor insert modes.
     * @since 3.0
     */
    private List fLegalInsertModes= null;
    /**
     * The non-default caret.
     * @since 3.0
     */
    private Caret fNonDefaultCaret;
    /**
     * The image used in non-default caret.
     * @since 3.0
     */
    private Image fNonDefaultCaretImage;
    /**
     * The styled text's initial caret.
     * @since 3.0
     */
    private Caret fInitialCaret;
    /**
     * The operation approver used to warn on undoing of non-local operations.
     * @since 3.1
     */
    private IOperationApprover fNonLocalOperationApprover;
    /**
     * The operation approver used to warn of linear undo violations.
     * @since 3.1
     */
    private IOperationApprover fLinearUndoViolationApprover;
    /**
     * This editor's memento holding data for restoring it after restart.
     * @since 3.3
     */
    private IMemento fMementoToRestore;
    /**
     * This editor's savable.
     * @since 3.3
     */
    private TextEditorSavable fSavable;
    /**
     * Tells whether text drag and drop is enabled.
     * @since 3.3
     */
    private boolean fIsTextDragAndDropEnabled= false;
    /**
     * Tells whether text drag and drop has been installed on the control.
     * @since 3.3
     */
    private boolean fIsTextDragAndDropInstalled= false;
    /**
     * Helper token to decide whether drag and
     * drop happens inside the same editor.
     * @since 3.3
     */
    private Object fTextDragAndDropToken;
    /**
     * The information presenter.
     * @since 3.3
     */
    private InformationPresenter fInformationPresenter;


    /**
     * Creates a new text editor. If not explicitly set, this editor uses
     * a <code>SourceViewerConfiguration</code> to configure its
     * source viewer. This viewer does not have a range indicator installed,
     * nor any menu id set. By default, the created editor runs in 1.0 context
     * menu registration compatibility mode.
     */
    protected AbstractTextEditor() {
        super();
        fEditorContextMenuId= null;
        fRulerContextMenuId= null;
        fHelpContextId= null;
    }

    /*
     * @see ITextEditor#getDocumentProvider()
     */
    public IDocumentProvider getDocumentProvider() {
        return fExplicitDocumentProvider;
    }

    /**
     * Returns the editor's range indicator. May return <code>null</code> if no
     * range indicator is installed.
     *
     * @return the editor's range indicator which may be <code>null</code>
     */
    protected final Annotation getRangeIndicator() {
        return fRangeIndicator;
    }

    /**
     * Returns the editor's source viewer configuration. May return <code>null</code>
     * before the editor's part has been created and after disposal.
     *
     * @return the editor's source viewer configuration which may be <code>null</code>
     */
    protected final SourceViewerConfiguration getSourceViewerConfiguration() {
        return fConfiguration;
    }

    /**
     * Returns the editor's source viewer. May return <code>null</code> before
     * the editor's part has been created and after disposal.
     *
     * @return the editor's source viewer which may be <code>null</code>
     */
    protected final ISourceViewer getSourceViewer() {
        return fSourceViewer;
    }

    /**
     * Returns the editor's vertical ruler. May return <code>null</code> before
     * the editor's part has been created and after disposal.
     *
     * @return the editor's vertical ruler which may be <code>null</code>
     */
    protected final IVerticalRuler getVerticalRuler() {
        return fVerticalRuler;
    }

    /**
     * Returns the editor's context menu id. May return <code>null</code> before
     * the editor's part has been created.
     *
     * @return the editor's context menu id which may be <code>null</code>
     */
    protected final String getEditorContextMenuId() {
        return fEditorContextMenuId;
    }

    /**
     * Returns the ruler's context menu id. May return <code>null</code> before
     * the editor's part has been created.
     *
     * @return the ruler's context menu id which may be <code>null</code>
     */
    protected final String getRulerContextMenuId() {
        return fRulerContextMenuId;
    }

    /**
     * Returns the editor's help context id or <code>null</code> if none has
     * been set.
     *
     * @return the editor's help context id which may be <code>null</code>
     */
    protected final String getHelpContextId() {
        return fHelpContextId;
    }

    /**
     * Returns this editor's preference store or <code>null</code> if none has
     * been set.
     *
     * @return this editor's preference store which may be <code>null</code>
     */
    protected final IPreferenceStore getPreferenceStore() {
        return fPreferenceStore;
    }

    /**
     * Sets this editor's document provider. This method must be
     * called before the editor's control is created.
     *
     * @param provider the document provider
     */
    protected void setDocumentProvider(IDocumentProvider provider) {
        fExplicitDocumentProvider= provider;
    }

    /**
     * Sets this editor's source viewer configuration used to configure its
     * internal source viewer. This method must be called before the editor's
     * control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>.
     *
     * @param configuration the source viewer configuration object
     */
    protected void setSourceViewerConfiguration(SourceViewerConfiguration configuration) {
        Assert.isNotNull(configuration);
        fConfiguration= configuration;
    }

    /**
     * Sets the annotation which this editor uses to represent the highlight
     * range if the editor is configured to show the entire document. If the
     * range indicator is not set, this editor will not show a range indication.
     *
     * @param rangeIndicator the annotation
     */
    protected void setRangeIndicator(Annotation rangeIndicator) {
        Assert.isNotNull(rangeIndicator);
        fRangeIndicator= rangeIndicator;
    }

    /**
     * Sets this editor's context menu id.
     *
     * @param contextMenuId the context menu id
     */
    protected void setEditorContextMenuId(String contextMenuId) {
        Assert.isNotNull(contextMenuId);
        fEditorContextMenuId= contextMenuId;
    }

    /**
     * Sets the ruler's context menu id.
     *
     * @param contextMenuId the context menu id
     */
    protected void setRulerContextMenuId(String contextMenuId) {
        Assert.isNotNull(contextMenuId);
        fRulerContextMenuId= contextMenuId;
    }

    /**
     * Sets the context menu registration 1.0 compatibility mode. (See class
     * description for more details.)
     *
     * @param compatible <code>true</code> if compatibility mode is enabled
     * @since 2.0
     */
    protected final void setCompatibilityMode(boolean compatible) {
        fCompatibilityMode= compatible;
    }

    /**
     * Sets the editor's help context id.
     *
     * @param helpContextId the help context id
     */
    protected void setHelpContextId(String helpContextId) {
        Assert.isNotNull(helpContextId);
        fHelpContextId= helpContextId;
    }

    /**
     * Sets the key binding scopes for this editor.
     *
     * @param scopes a non-empty array of key binding scope identifiers
     * @since 2.1
     */
    protected void setKeyBindingScopes(String [] scopes) {
        Assert.isTrue(scopes != null && scopes.length > 0);
        fKeyBindingScopes= scopes;
    }

    /**
     * Sets this editor's preference store. This method must be
     * called before the editor's control is created.
     *
     * @param store the preference store or <code>null</code> to remove the
     * preference store
     */
    protected void setPreferenceStore(IPreferenceStore store) {
        if (fPreferenceStore != null)
            fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);

        fPreferenceStore= store;

        if (fPreferenceStore != null)
            fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
    }

    /*
     * @see ITextEditor#isEditable()
     */
    public boolean isEditable() {
        IDocumentProvider provider= getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension) {
            IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
            return extension.isModifiable(getEditorInput());
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns <code>null</code> after disposal.
     * </p>
     *
     * @return the selection provider or <code>null</code> if the editor has
     * been disposed
     */
    public ISelectionProvider getSelectionProvider() {
        return fSelectionProvider;
    }

    /**
     * Remembers the current selection of this editor. This method is called when, e.g.,
     * the content of the editor is about to be reverted to the saved state. This method
     * remembers the selection in a semantic format, i.e., in a format which allows to
     * restore the selection even if the originally selected text is no longer part of the
     * editor's content.
     * <p>
     * Subclasses should implement this method including all necessary state. This
     * default implementation remembers the textual range only and is thus purely
     * syntactic.</p>
     *
     * @see #restoreSelection()
     * @since 2.0
     */
    protected void rememberSelection() {
        fRememberedSelection= doGetSelection();
    }

    /**
     * Returns the current selection.
     * @return ISelection
     * @since 2.1
     */
    protected ISelection doGetSelection() {
        ISelectionProvider sp= null;
        if (fSourceViewer != null)
            sp= fSourceViewer.getSelectionProvider();
        return (sp == null ? null : sp.getSelection());
    }

    /**
     * Restores a selection previously remembered by <code>rememberSelection</code>.
     * Subclasses may reimplement this method and thereby semantically adapt the
     * remembered selection. This default implementation just selects the
     * remembered textual range.
     *
     * @see #rememberSelection()
     * @since 2.0
     */
    protected void restoreSelection() {
        if (fRememberedSelection instanceof ITextSelection) {
            ITextSelection textSelection= (ITextSelection)fRememberedSelection;
            if (isValidSelection(textSelection.getOffset(), textSelection.getLength()))
                doSetSelection(fRememberedSelection);
        }
        fRememberedSelection= null;
    }

    /**
     * Tells whether the given selection is valid.
     *
     * @param offset the offset of the selection
     * @param length the length of the selection
     * @return <code>true</code> if the selection is valid
     * @since 2.1
     */
    private boolean isValidSelection(int offset, int length) {
        IDocumentProvider provider= getDocumentProvider();
        if (provider != null) {
            IDocument document= provider.getDocument(getEditorInput());
            if (document != null) {
                int end= offset + length;
                int documentLength= document.getLength();
                return 0 <= offset && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
            }
        }
        return false;
    }

    /**
     * Sets the given selection.
     * @param selection
     * @since 2.1
     */
    protected void doSetSelection(ISelection selection) {
        if (selection instanceof ITextSelection) {
            ITextSelection textSelection= (ITextSelection) selection;
            selectAndReveal(textSelection.getOffset(), textSelection.getLength());
        }
    }

    /**
     * Creates and returns the listener on this editor's context menus.
     *
     * @return the menu listener
     */
    protected final IMenuListener getContextMenuListener() {
        if (fMenuListener == null) {
            fMenuListener= new IMenuListener() {

                public void menuAboutToShow(IMenuManager menu) {
                    String id= menu.getId();
                    if (getRulerContextMenuId().equals(id)) {
                        setFocus();
                        rulerContextMenuAboutToShow(menu);
                    } else if (getEditorContextMenuId().equals(id)) {
                        setFocus();
                        editorContextMenuAboutToShow(menu);
                    }
                }
            };
        }
        return fMenuListener;
    }

    /**
     * Creates and returns the listener on this editor's vertical ruler.
     *
     * @return the mouse listener
     */
    protected final MouseListener getRulerMouseListener() {
        if (fMouseListener == null) {
            fMouseListener= new MouseListener() {

                private boolean fDoubleClicked= false;
                private final int fDoubleClickTime= Display.getDefault().getDoubleClickTime();
                private long fMouseUpDelta= 0;

                private void triggerAction(String actionID) {
                    IAction action= getAction(actionID);
                    if (action != null) {
                        if (action instanceof IUpdate)
                            ((IUpdate) action).update();
                        if (action.isEnabled())
                            action.run();
                    }
                }

                public void mouseUp(final MouseEvent e) {
                    setFocus();
                    final int delay= fDoubleClickTime - (int)(System.currentTimeMillis() - fMouseUpDelta);
                    if (1 != e.button)
                        return;
                    
                    Runnable runnable= new Runnable () {
                        public void run() {
                            if (!fDoubleClicked)
                                triggerAction(ITextEditorActionConstants.RULER_CLICK);
                        }
                    };
                    if (delay <= 0)
                        runnable.run();
                    else
                        Display.getDefault().timerExec(delay, runnable);
                }

                public void mouseDoubleClick(MouseEvent e) {
                    if (1 == e.button) {
                        fDoubleClicked= true;
                        triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK);
                    }
                }

                public void mouseDown(MouseEvent e) {
                    fMouseUpDelta= System.currentTimeMillis();
                    fDoubleClicked= false;
                    StyledText text= fSourceViewer.getTextWidget();
                    if (text != null && !text.isDisposed()) {
                            Display display= text.getDisplay();
                            Point location= display.getCursorLocation();
                            fRulerContextMenu.setLocation(location.x, location.y);
                    }
                }
            };
        }
        return fMouseListener;
    }

    /**
     * Returns this editor's selection changed listener to be installed
     * on the editor's source viewer.
     *
     * @return the listener
     */
    protected final ISelectionChangedListener getSelectionChangedListener() {
        if (fSelectionChangedListener == null) {
            fSelectionChangedListener= new ISelectionChangedListener() {

                private Runnable fRunnable= new Runnable () {
                    public void run() {
                        // check whether editor has not been disposed yet
 if (fSourceViewer != null && fSourceViewer.getDocument() != null) {
                            updateSelectionDependentActions();
                        }
                    }
                };

                private Display fDisplay;

                public void selectionChanged(SelectionChangedEvent event) {
                    if (fDisplay == null)
                        fDisplay= getSite().getShell().getDisplay();
                    fDisplay.asyncExec(fRunnable);
                    handleCursorPositionChanged();
                }
            };
        }

        return fSelectionChangedListener;
    }

    /**
     * Returns this editor's "cursor" listener to be installed on the editor's
     * source viewer. This listener is listening to key and mouse button events.
     * It triggers the updating of the status line by calling
     * <code>handleCursorPositionChanged()</code>.
     *
     * @return the listener
     * @since 2.0
     */
    protected final ICursorListener getCursorListener() {
        if (fCursorListener == null) {
            fCursorListener= new ICursorListener() {

                public void keyPressed(KeyEvent e) {
                    handleCursorPositionChanged();
                }

                public void keyReleased(KeyEvent e) {
                }

                public void mouseDoubleClick(MouseEvent e) {
                }

                public void mouseDown(MouseEvent e) {
                }

                public void mouseUp(MouseEvent e) {
                    handleCursorPositionChanged();
                }
            };
        }
        return fCursorListener;
    }

    /**
     * Implements the <code>init</code> method of <code>IEditorPart</code>.
     * Subclasses replacing <code>init</code> may choose to call this method in
     * their implementation.
     *
     * @param window the workbench window
     * @param site the editor's site
     * @param input the editor input for the editor being created
     * @throws PartInitException if {@link #doSetInput(IEditorInput)} fails or gets canceled
     *
     * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
     * @since 2.1
     */
    protected final void internalInit(IWorkbenchWindow window, final IEditorSite site, final IEditorInput input) throws PartInitException {

        IRunnableWithProgress runnable= new IRunnableWithProgress() {
            public void run(IProgressMonitor monitor) throws InvocationTargetException , InterruptedException {
                try {

                    if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
                        IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) getDocumentProvider();
                        extension.setProgressMonitor(monitor);
                    }

                    doSetInput(input);

                } catch (CoreException x) {
                    throw new InvocationTargetException (x);
                } finally {
                    if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
                        IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) getDocumentProvider();
                        extension.setProgressMonitor(null);
                    }
                }
            }
        };

        try {
// When using the progress service always a modal dialog pops up. The site should be asked for a runnable context
// which could be the workbench window or the progress service, depending on what the site represents.
// getSite().getWorkbenchWindow().getWorkbench().getProgressService().run(false, true, runnable);

            getSite().getWorkbenchWindow().run(false, true, runnable);

        } catch (InterruptedException x) {
        } catch (InvocationTargetException x) {
            Throwable t= x.getTargetException();
            if (t instanceof CoreException) {
                /*
                /* XXX: Remove unpacking of CoreException once the following bug is
                 * fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640
                 */
                CoreException e= (CoreException)t;
                IStatus status= e.getStatus();
                if (status.getException() != null)
                    throw new PartInitException(status);
                throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t));
            }
            throw new PartInitException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_init, t));
        }
    }

    /*
     * @see IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
     */
    public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {

        setSite(site);

        internalInit(site.getWorkbenchWindow(), site, input);
        fActivationListener= new ActivationListener(site.getWorkbenchWindow().getPartService());
    }

    /**
     * Creates the vertical ruler to be used by this editor.
     * Subclasses may re-implement this method.
     *
     * @return the vertical ruler
     */
    protected IVerticalRuler createVerticalRuler() {
        return new VerticalRuler(VERTICAL_RULER_WIDTH);
    }
    
    /**
     * Adds enabled ruler contributions to the vertical ruler.
     * <p>
     * Clients may extend or replace.</p>
     *
     * @param ruler the composite ruler to add contributions to
     * @since 3.3
     */
    protected void updateContributedRulerColumns(CompositeRuler ruler) {
        IColumnSupport support= (IColumnSupport)getAdapter(IColumnSupport.class);
        if (support == null)
            return;
        
        RulerColumnPreferenceAdapter adapter= null;
        if (fPreferenceStore != null)
            adapter= new RulerColumnPreferenceAdapter(getPreferenceStore(), PREFERENCE_RULER_CONTRIBUTIONS);
        
        RulerColumnRegistry registry= RulerColumnRegistry.getDefault();
        List descriptors= registry.getColumnDescriptors();
        for (Iterator it= descriptors.iterator(); it.hasNext();) {
            final RulerColumnDescriptor descriptor= (RulerColumnDescriptor) it.next();
            support.setColumnVisible(descriptor, adapter == null || adapter.isEnabled(descriptor));
        }
    }

    /**
     * Creates the column support to be used by this editor to manage the
     * contributed ruler columns.
     * Subclasses may re-implement this method using the {@link ColumnSupport},
     * e.g. by returning <code>new ColumnSupport(this, RulerColumnRegistry.getDefault());</code>.
     * <p>
     * <strong>Note:</strong> If you override this method to provide column support you will
     * also need to override {@link #createVerticalRuler()} to return a {@link CompositeRuler}.</p>
     * <p>
     * Out of the box this class does not install this support and hence this
     * implementation always returns <code>null</code>.</p>
     *
     * @return the column support or <code>null</code> if none
     * @since 3.3
     */
    protected IColumnSupport createColumnSupport() {
        return null;
    }

    /**
     * Creates the source viewer to be used by this editor.
     * Subclasses may re-implement this method.
     *
     * @param parent the parent control
     * @param ruler the vertical ruler
     * @param styles style bits, <code>SWT.WRAP</code> is currently not supported
     * @return the source viewer
     */
    protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
        return new SourceViewer(parent, ruler, styles);
    }

    /**
     * Initializes the drag and drop support for the given viewer based on
     * provided editor adapter for drop target listeners.
     *
     * @param viewer the viewer
     * @since 3.0
     */
    protected void initializeDragAndDrop(ISourceViewer viewer) {
        IDragAndDropService dndService= (IDragAndDropService)getSite().getService(IDragAndDropService.class);
        if (dndService == null)
            return;
        
        ITextEditorDropTargetListener listener= (ITextEditorDropTargetListener) getAdapter(ITextEditorDropTargetListener.class);

        if (listener == null) {
            Object object= Platform.getAdapterManager().loadAdapter(this, "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$
 if (object instanceof ITextEditorDropTargetListener)
                listener= (ITextEditorDropTargetListener)object;
        }

        if (listener != null)
            dndService.addMergedDropTarget(viewer.getTextWidget(), DND.DROP_MOVE | DND.DROP_COPY, listener.getTransfers(), listener);

        IPreferenceStore store= getPreferenceStore();
        if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
            installTextDragAndDrop(viewer);

    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>IWorkbenchPart</code> method creates the vertical ruler and
     * source viewer.
     * <p>
     * Subclasses may extend this method. Besides extending this method, the
     * behavior of <code>createPartControl</code> may be customized by
     * calling, extending or replacing the following methods: <br>
     * Subclasses may supply customized implementations for some members using
     * the following methods before <code>createPartControl</code> is invoked:
     * <ul>
     * <li>
     * {@linkplain #setSourceViewerConfiguration(SourceViewerConfiguration) setSourceViewerConfiguration}
     * to supply a custom source viewer configuration,</li>
     * <li>{@linkplain #setRangeIndicator(Annotation) setRangeIndicator} to
     * provide a range indicator,</li>
     * <li>{@linkplain #setHelpContextId(String) setHelpContextId} to provide a
     * help context id,</li>
     * <li>{@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}
     * to set a custom context menu id,</li>
     * <li>{@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} to
     * set a custom ruler context menu id.</li>
     * </ul>
     * <br>
     * Subclasses may replace the following methods called from within
     * <code>createPartControl</code>:
     * <ul>
     * <li>{@linkplain #createVerticalRuler() createVerticalRuler} to supply a
     * custom vertical ruler,</li>
     * <li>{@linkplain #createSourceViewer(Composite, IVerticalRuler, int) createSourceViewer}
     * to supply a custom source viewer,</li>
     * <li>{@linkplain #getSelectionProvider() getSelectionProvider} to supply
     * a custom selection provider.</li>
     * </ul>
     * <br>
     * Subclasses may extend the following methods called from within
     * <code>createPartControl</code>:
     * <ul>
     * <li>
     * {@linkplain #initializeViewerColors(ISourceViewer) initializeViewerColors}
     * to customize the viewer color scheme (may also be replaced),</li>
     * <li>
     * {@linkplain #initializeDragAndDrop(ISourceViewer) initializeDragAndDrop}
     * to customize drag and drop (may also be replaced),</li>
     * <li>{@linkplain #createNavigationActions() createNavigationActions} to
     * add navigation actions,</li>
     * <li>{@linkplain #createActions() createActions} to add text editor
     * actions.</li>
     * </ul>
     * </p>
     *
     * @param parent the parent composite
     */
    public void createPartControl(Composite parent) {

        fVerticalRuler= createVerticalRuler();

        int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
        fSourceViewer= createSourceViewer(parent, fVerticalRuler, styles);

        if (fConfiguration == null)
            fConfiguration= new SourceViewerConfiguration();
        fSourceViewer.configure(fConfiguration);

        if (fRangeIndicator != null)
            fSourceViewer.setRangeIndicator(fRangeIndicator);

        fSourceViewer.addTextListener(fTextListener);
        fSourceViewer.addTextInputListener(fTextListener);
        getSelectionProvider().addSelectionChangedListener(getSelectionChangedListener());

        initializeViewerFont(fSourceViewer);
        initializeViewerColors(fSourceViewer);
        initializeFindScopeColor(fSourceViewer);
        initializeDragAndDrop(fSourceViewer);

        StyledText styledText= fSourceViewer.getTextWidget();

        /* gestures commented out until proper solution (i.e. preference page) can be found
         * for bug # 28417:
         *
        final Map gestureMap= new HashMap();

        gestureMap.put("E", "org.eclipse.ui.navigate.forwardHistory");
        gestureMap.put("N", "org.eclipse.ui.file.save");
        gestureMap.put("NW", "org.eclipse.ui.file.saveAll");
        gestureMap.put("S", "org.eclipse.ui.file.close");
        gestureMap.put("SW", "org.eclipse.ui.file.closeAll");
        gestureMap.put("W", "org.eclipse.ui.navigate.backwardHistory");
        gestureMap.put("EN", "org.eclipse.ui.edit.copy");
        gestureMap.put("ES", "org.eclipse.ui.edit.paste");
        gestureMap.put("EW", "org.eclipse.ui.edit.cut");

        Capture capture= Capture.create();
        capture.setControl(styledText);

        capture.addCaptureListener(new CaptureListener() {
            public void gesture(Gesture gesture) {
                if (gesture.getPen() == 3) {
                    String actionId= (String) gestureMap.get(Util.recognize(gesture.getPoints(), 20));

                    if (actionId != null) {
                        IKeyBindingService keyBindingService= getEditorSite().getKeyBindingService();

                        if (keyBindingService instanceof KeyBindingService) {
                            IAction action= ((KeyBindingService) keyBindingService).getAction(actionId);

                            if (action != null) {
                                if (action instanceof IUpdate)
                                    ((IUpdate) action).update();

                                if (action.isEnabled())
                                    action.run();
                            }
                        }

                        return;
                    }

                    fTextContextMenu.setVisible(true);
                }
            };
        });
        */

        styledText.addMouseListener(getCursorListener());
        styledText.addKeyListener(getCursorListener());

        if (getHelpContextId() != null)
            PlatformUI.getWorkbench().getHelpSystem().setHelp(styledText, getHelpContextId());


        String id= fEditorContextMenuId != null ? fEditorContextMenuId : DEFAULT_EDITOR_CONTEXT_MENU_ID;

        MenuManager manager= new MenuManager(id, id);
        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(getContextMenuListener());
        fTextContextMenu= manager.createContextMenu(styledText);

        // comment this line if using gestures, above.
 styledText.setMenu(fTextContextMenu);

        if (fEditorContextMenuId != null)
            getEditorSite().registerContextMenu(fEditorContextMenuId, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
        else if (fCompatibilityMode)
            getEditorSite().registerContextMenu(DEFAULT_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());

        if ((fEditorContextMenuId != null && fCompatibilityMode) || fEditorContextMenuId == null) {
            String partId= getEditorSite().getId();
            if (partId != null)
                getEditorSite().registerContextMenu(partId + ".EditorContext", manager, getSelectionProvider(), isEditorInputIncludedInContextMenu()); //$NON-NLS-1$
 }

        if (fEditorContextMenuId == null)
            fEditorContextMenuId= DEFAULT_EDITOR_CONTEXT_MENU_ID;


        id= fRulerContextMenuId != null ? fRulerContextMenuId : DEFAULT_RULER_CONTEXT_MENU_ID;
        manager= new MenuManager(id, id);
        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(getContextMenuListener());

        Control rulerControl= fVerticalRuler.getControl();
        fRulerContextMenu= manager.createContextMenu(rulerControl);
        rulerControl.setMenu(fRulerContextMenu);
        rulerControl.addMouseListener(getRulerMouseListener());

        if (fRulerContextMenuId != null)
            getEditorSite().registerContextMenu(fRulerContextMenuId, manager, getSelectionProvider(), false);
        else if (fCompatibilityMode)
            getEditorSite().registerContextMenu(DEFAULT_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);

        if ((fRulerContextMenuId != null && fCompatibilityMode) || fRulerContextMenuId == null) {
            String partId= getSite().getId();
            if (partId != null)
                getEditorSite().registerContextMenu(partId + ".RulerContext", manager, getSelectionProvider(), false); //$NON-NLS-1$
 }

        if (fRulerContextMenuId == null)
            fRulerContextMenuId= DEFAULT_RULER_CONTEXT_MENU_ID;

        getSite().setSelectionProvider(getSelectionProvider());

        fSelectionListener= new SelectionListener();
        fSelectionListener.install(getSelectionProvider());
        fSelectionListener.setDocument(getDocumentProvider().getDocument(getEditorInput()));

        initializeActivationCodeTrigger();

        createNavigationActions();
        createAccessibilityActions();
        createActions();

        initializeSourceViewer(getEditorInput());
        
        /* since 3.2 - undo redo actions should be created after
         * the source viewer is initialized, so that the undo manager
         * can obtain its undo context from its document.
         */
        createUndoRedoActions();

        JFaceResources.getFontRegistry().addListener(fFontPropertyChangeListener);
        
        IVerticalRuler ruler= getVerticalRuler();
        if (ruler instanceof CompositeRuler)
            updateContributedRulerColumns((CompositeRuler) ruler);
        
        IInformationControlCreator informationControlCreator= new IInformationControlCreator() {
            public IInformationControl createInformationControl(Shell shell) {
                boolean cutDown= false;
                int style= cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL);
                return new DefaultInformationControl(shell, SWT.RESIZE | SWT.TOOL, style, new HTMLTextPresenter(cutDown));
            }
        };
        
        fInformationPresenter= new InformationPresenter(informationControlCreator);
        fInformationPresenter.setSizeConstraints(60, 10, true, true);
        fInformationPresenter.install(getSourceViewer());
        fInformationPresenter.setDocumentPartitioning(getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()));
        
    }
    
    /**
     * Installs text drag and drop on the given source viewer.
     *
     * @param viewer the viewer
     * @since 3.3
     */
    protected void installTextDragAndDrop(final ISourceViewer viewer) {
        if (fIsTextDragAndDropEnabled || viewer == null)
            return;
        
        if (fIsTextDragAndDropInstalled) {
            fIsTextDragAndDropEnabled= true;
            return;
        }
        
        final IDragAndDropService dndService= (IDragAndDropService)getSite().getService(IDragAndDropService.class);
        if (dndService == null)
            return;

        fIsTextDragAndDropEnabled= true;
        
        final StyledText st= viewer.getTextWidget();
        
        // Install drag source
 final ISelectionProvider selectionProvider= viewer.getSelectionProvider();
        final DragSource source= new DragSource(st, DND.DROP_COPY | DND.DROP_MOVE);
        source.setTransfer(new Transfer[] {TextTransfer.getInstance()});
        source.addDragListener(new DragSourceAdapter() {
            String fSelectedText;
            Point fSelection;
            public void dragStart(DragSourceEvent event) {
                fTextDragAndDropToken= null;
                
                // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
 if (!fIsTextDragAndDropEnabled) {
                    event.doit= false;
                    event.image= null;
                    return;
                }
                
                try {
                    fSelection= st.getSelection();
                    int offset= st.getOffsetAtLocation(new Point(event.x, event.y));
                    Point p= st.getLocationAtOffset(offset);
                    if (p.x > event.x)
                        offset--;
                    event.doit= offset > fSelection.x && offset < fSelection.y;

                    ISelection selection= selectionProvider.getSelection();
                    if (selection instanceof ITextSelection)
                        fSelectedText= ((ITextSelection)selection).getText();
                    else // fallback to widget
 fSelectedText= st.getSelectionText();
                } catch (IllegalArgumentException ex) {
                    event.doit= false;
                }
            }
            
            public void dragSetData(DragSourceEvent event) {
                event.data= fSelectedText;
                fTextDragAndDropToken= this; // Can be any non-null object
 }
            
            public void dragFinished(DragSourceEvent event) {
                try {
                    if (event.detail == DND.DROP_MOVE && validateEditorInputState()) {
                        Point newSelection= st.getSelection();
                        int length= fSelection.y - fSelection.x;
                        int delta= 0;
                        if (newSelection.x < fSelection.x)
                            delta= length;
                        st.replaceTextRange(fSelection.x + delta, length, ""); //$NON-NLS-1$

                        if (fTextDragAndDropToken == null) {
                            // Move in same editor - end compound change
 IRewriteTarget target= (IRewriteTarget)getAdapter(IRewriteTarget.class);
                            if (target != null)
                                target.endCompoundChange();
                        }
                        
                    }
                } finally {
                    fTextDragAndDropToken= null;
                }
            }
        });
        
        // Install drag target
 DropTargetListener dropTargetListener= new DropTargetAdapter() {
            
            private Point fSelection;
            
            public void dragEnter(DropTargetEvent event) {
                fTextDragAndDropToken= null;
                fSelection= st.getSelection();
                
                // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
 if (!fIsTextDragAndDropEnabled) {
                    event.detail= DND.DROP_NONE;
                    event.feedback= DND.FEEDBACK_NONE;
                    return;
                }

                if (event.detail == DND.DROP_DEFAULT)
                    event.detail= DND.DROP_MOVE;
            }
            
            public void dragOperationChanged(DropTargetEvent event) {
                if (!fIsTextDragAndDropEnabled) {
                    event.detail= DND.DROP_NONE;
                    event.feedback= DND.FEEDBACK_NONE;
                    return;
                }
                
                if (event.detail == DND.DROP_DEFAULT)
                    event.detail= DND.DROP_MOVE;
            }
            
            public void dragOver(DropTargetEvent event) {
                
                // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
 if (!fIsTextDragAndDropEnabled) {
                    event.feedback= DND.FEEDBACK_NONE;
                    return;
                }
                
                event.feedback |= DND.FEEDBACK_SCROLL;
            }
            
            public void drop(DropTargetEvent event) {
                try {
                    if (!fIsTextDragAndDropEnabled)
                        return;
    
                    if (fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
                        // Move in same editor
 int caretOffset= st.getCaretOffset();
                        if (fSelection.x <= caretOffset && caretOffset <= fSelection.y) {
                            event.detail= DND.DROP_NONE;
                            return;
                        }
                        
                        // Start compound change
 IRewriteTarget target= (IRewriteTarget)getAdapter(IRewriteTarget.class);
                        if (target != null)
                            target.beginCompoundChange();
                    }
                    
                    if (!validateEditorInputState()) {
                        event.detail= DND.DROP_NONE;
                        return;
                    }
                    
                    String text= (String )event.data;
                    Point newSelection= st.getSelection();
                    try {
                        int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x);
                        viewer.getDocument().replace(modelOffset, 0, text);
                    } catch (BadLocationException e) {
                        return;
                    }
                    st.setSelectionRange(newSelection.x, text.length());
                } finally {
                    fTextDragAndDropToken= null;
                }
            }
        };
        dndService.addMergedDropTarget(st, DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] {TextTransfer.getInstance()}, dropTargetListener);

        fIsTextDragAndDropInstalled= true;
        fIsTextDragAndDropEnabled= true;
    }

    /**
     * Uninstalls text drag and drop from the given source viewer.
     *
     * @param viewer the viewer
     * @since 3.3
     */
    protected void uninstallTextDragAndDrop(ISourceViewer viewer) {
        fIsTextDragAndDropEnabled= false;
    }
    
    /**
     * Tells whether the editor input should be included when adding object
     * contributions to this editor's context menu.
     * <p>
     * This implementation always returns <code>true</code>.
     * </p>
     *
     * @return <code>true</code> if the editor input should be considered
     * @since 3.2
     */
    protected boolean isEditorInputIncludedInContextMenu() {
        return true;
    }

    /**
     * Initializes the activation code trigger.
     *
     * @since 2.1
     */
    private void initializeActivationCodeTrigger() {
        fActivationCodeTrigger.install();
        fActivationCodeTrigger.setScopes(fKeyBindingScopes);
    }

    /**
     * Initializes the given viewer's font.
     *
     * @param viewer the viewer
     * @since 2.0
     */
    private void initializeViewerFont(ISourceViewer viewer) {

        boolean isSharedFont= true;
        Font font= null;
        String symbolicFontName= getSymbolicFontName();

        if (symbolicFontName != null)
            font= JFaceResources.getFont(symbolicFontName);
        else if (fPreferenceStore != null) {
            // Backward compatibility
 if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) {
                FontData data= PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT);

                if (data != null) {
                    isSharedFont= false;
                    font= new Font(viewer.getTextWidget().getDisplay(), data);
                }
            }
        }
        if (font == null)
            font= JFaceResources.getTextFont();

        setFont(viewer, font);

        if (fFont != null) {
            fFont.dispose();
            fFont= null;
        }

        if (!isSharedFont)
            fFont= font;
    }

    /**
     * Sets the font for the given viewer sustaining selection and scroll position.
     *
     * @param sourceViewer the source viewer
     * @param font the font
     * @since 2.0
     */
    private void setFont(ISourceViewer sourceViewer, Font font) {
        if (sourceViewer.getDocument() != null) {

            Point selection= sourceViewer.getSelectedRange();
            int topIndex= sourceViewer.getTopIndex();

            StyledText styledText= sourceViewer.getTextWidget();
            Control parent= styledText;
            if (sourceViewer instanceof ITextViewerExtension) {
                ITextViewerExtension extension= (ITextViewerExtension) sourceViewer;
                parent= extension.getControl();
            }

            parent.setRedraw(false);

            styledText.setFont(font);

            if (fVerticalRuler instanceof IVerticalRulerExtension) {
                IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
                e.setFont(font);
            }

            sourceViewer.setSelectedRange(selection.x , selection.y);
            sourceViewer.setTopIndex(topIndex);

            if (parent instanceof Composite) {
                Composite composite= (Composite) parent;
                composite.layout(true);
            }

            parent.setRedraw(true);


        } else {

            StyledText styledText= sourceViewer.getTextWidget();
            styledText.setFont(font);

            if (fVerticalRuler instanceof IVerticalRulerExtension) {
                IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
                e.setFont(font);
            }
        }
    }

    /**
     * Creates a color from the information stored in the given preference store.
     * Returns <code>null</code> if there is no such information available.
     *
     * @param store the store to read from
     * @param key the key used for the lookup in the preference store
     * @param display the display used create the color
     * @return the created color according to the specification in the preference store
     * @since 2.0
     */
    private Color createColor(IPreferenceStore store, String key, Display display) {

        RGB rgb= null;

        if (store.contains(key)) {

            if (store.isDefault(key))
                rgb= PreferenceConverter.getDefaultColor(store, key);
            else
                rgb= PreferenceConverter.getColor(store, key);

            if (rgb != null)
                return new Color(display, rgb);
        }

        return null;
    }

    /**
     * Initializes the fore- and background colors of the given viewer for both
     * normal and selected text.
     *
     * @param viewer the viewer to be initialized
     * @since 2.0
     */
    protected void initializeViewerColors(ISourceViewer viewer) {

        IPreferenceStore store= getPreferenceStore();
        if (store != null) {

            StyledText styledText= viewer.getTextWidget();

            // ----------- foreground color --------------------
 Color color= store.getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
                ? null
                : createColor(store, PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
            styledText.setForeground(color);

            if (fForegroundColor != null)
                fForegroundColor.dispose();

            fForegroundColor= color;

            // ---------- background color ----------------------
 color= store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
                ? null
                : createColor(store, PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
            styledText.setBackground(color);

            if (fBackgroundColor != null)
                fBackgroundColor.dispose();

            fBackgroundColor= color;

            // ----------- selection foreground color --------------------
 color= store.getBoolean(PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
                ? null
                : createColor(store, PREFERENCE_COLOR_SELECTION_FOREGROUND, styledText.getDisplay());
            styledText.setSelectionForeground(color);

            if (fSelectionForegroundColor != null)
                fSelectionForegroundColor.dispose();

            fSelectionForegroundColor= color;

            // ---------- selection background color ----------------------
 color= store.getBoolean(PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
                ? null
                : createColor(store, PREFERENCE_COLOR_SELECTION_BACKGROUND, styledText.getDisplay());
            styledText.setSelectionBackground(color);

            if (fSelectionBackgroundColor != null)
                fSelectionBackgroundColor.dispose();

            fSelectionBackgroundColor= color;
        }
    }

    /**
     * Initializes the background color used for highlighting the document ranges
     * defining search scopes.
     *
     * @param viewer the viewer to initialize
     * @since 2.0
     */
    private void initializeFindScopeColor(ISourceViewer viewer) {

        IPreferenceStore store= getPreferenceStore();
        if (store != null) {

            StyledText styledText= viewer.getTextWidget();

            Color color= createColor(store, PREFERENCE_COLOR_FIND_SCOPE, styledText.getDisplay());

            IFindReplaceTarget target= viewer.getFindReplaceTarget();
            if (target != null && target instanceof IFindReplaceTargetExtension)
                ((IFindReplaceTargetExtension) target).setScopeHighlightColor(color);

            if (fFindScopeHighlightColor != null)
                fFindScopeHighlightColor.dispose();

            fFindScopeHighlightColor= color;
        }
    }


    /**
     * Initializes the editor's source viewer based on the given editor input.
     *
     * @param input the editor input to be used to initialize the source viewer
     */
    private void initializeSourceViewer(IEditorInput input) {

        IAnnotationModel model= getDocumentProvider().getAnnotationModel(input);
        IDocument document= getDocumentProvider().getDocument(input);

        if (document != null) {
            fSourceViewer.setDocument(document, model);
            fSourceViewer.setEditable(isEditable());
            fSourceViewer.showAnnotations(model != null);
        }

        if (fElementStateListener instanceof IElementStateListenerExtension) {
            IElementStateListenerExtension extension= (IElementStateListenerExtension) fElementStateListener;
            extension.elementStateValidationChanged(input, false);
        }

        if (fInitialCaret == null)
            fInitialCaret= fSourceViewer.getTextWidget().getCaret();

        if (fIsOverwriting)
            fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
        handleInsertModeChanged();
        
        if (isTabsToSpacesConversionEnabled())
            installTabsToSpacesConverter();
        
    }

    /**
     * Initializes the editor's title based on the given editor input.
     *
     * @param input the editor input to be used
     */
    private void initializeTitle(IEditorInput input) {

        Image oldImage= fTitleImage;
        fTitleImage= null;
        String title= ""; //$NON-NLS-1$

        if (input != null) {
            IEditorRegistry editorRegistry= PlatformUI.getWorkbench().getEditorRegistry();
            IEditorDescriptor editorDesc= editorRegistry.findEditor(getSite().getId());
            ImageDescriptor imageDesc= editorDesc != null ? editorDesc.getImageDescriptor() : null;

            fTitleImage= imageDesc != null ? imageDesc.createImage() : null;
            title= input.getName();
        }

        setTitleImage(fTitleImage);
        setPartName(title);

        firePropertyChange(PROP_DIRTY);

        if (oldImage != null && !oldImage.isDisposed())
            oldImage.dispose();
    }

    /**
     * Hook method for setting the document provider for the given input.
     * This default implementation does nothing. Clients may
     * reimplement.
     *
     * @param input the input of this editor.
     * @since 3.0
     */
    protected void setDocumentProvider(IEditorInput input) {
    }

    /**
     * If there is no explicit document provider set, the implicit one is
     * re-initialized based on the given editor input.
     *
     * @param input the editor input.
     */
    private void updateDocumentProvider(IEditorInput input) {

        IProgressMonitor rememberedProgressMonitor= null;

        IDocumentProvider provider= getDocumentProvider();
        if (provider != null) {
            provider.removeElementStateListener(fElementStateListener);
            if (provider instanceof IDocumentProviderExtension2) {
                IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
                rememberedProgressMonitor= extension.getProgressMonitor();
                extension.setProgressMonitor(null);
            }
        }

        setDocumentProvider(input);

        provider= getDocumentProvider();
        if (provider != null) {
            provider.addElementStateListener(fElementStateListener);
            if (provider instanceof IDocumentProviderExtension2) {
                IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
                extension.setProgressMonitor(rememberedProgressMonitor);
            }
        }
    }

    /**
     * Called directly from <code>setInput</code> and from within a workspace
     * runnable from <code>init</code>, this method does the actual setting
     * of the editor input. Closes the editor if <code>input</code> is
     * <code>null</code>. Disconnects from any previous editor input and its
     * document provider and connects to the new one.
     * <p>
     * Subclasses may extend.
     * </p>
     *
     * @param input the input to be set
     * @exception CoreException if input cannot be connected to the document
     * provider
     */
    protected void doSetInput(IEditorInput input) throws CoreException {
        ISaveablesLifecycleListener listener= (ISaveablesLifecycleListener)getSite().getService(ISaveablesLifecycleListener.class);
        if (listener == null)
            fSavable= null;
        
        if (input == null) {
            close(isSaveOnCloseNeeded());
            
            if (fSavable != null) {
                listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_CLOSE, getSaveables(), false));
                fSavable.disconnectEditor();
                fSavable= null;
            }
            
        } else {
            boolean mustSendLifeCycleEvent= false;
            if (fSavable != null) {
                listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_CLOSE, getSaveables(), false));
                fSavable.disconnectEditor();
                fSavable= null;
                mustSendLifeCycleEvent= true;
            }

            IEditorInput oldInput= getEditorInput();
            if (oldInput != null)
                getDocumentProvider().disconnect(oldInput);

            super.setInput(input);

            updateDocumentProvider(input);

            IDocumentProvider provider= getDocumentProvider();
            if (provider == null) {
                IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_no_provider, null);
                throw new CoreException(s);
            }

            provider.connect(input);

            initializeTitle(input);

            if (fSourceViewer != null) {
                initializeSourceViewer(input);

                // Reset the undo context for the undo and redo action handlers
 IAction undoAction= getAction(ITextEditorActionConstants.UNDO);
                IAction redoAction= getAction(ITextEditorActionConstants.REDO);
                boolean areOperationActionHandlersInstalled= undoAction instanceof OperationHistoryActionHandler && redoAction instanceof OperationHistoryActionHandler;
                IUndoContext undoContext= getUndoContext();
                if (undoContext != null && areOperationActionHandlersInstalled) {
                    ((OperationHistoryActionHandler)undoAction).setContext(undoContext);
                    ((OperationHistoryActionHandler)redoAction).setContext(undoContext);
                } else {
                    createUndoRedoActions();
                }
            }

            if (fIsOverwriting)
                toggleOverwriteMode();
            setInsertMode((InsertMode) getLegalInsertModes().get(0));
            updateCaret();

            updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);

            if (fSelectionListener != null)
                fSelectionListener.setDocument(getDocumentProvider().getDocument(input));
            
            IVerticalRuler ruler= getVerticalRuler();
            if (ruler instanceof CompositeRuler)
                updateContributedRulerColumns((CompositeRuler) ruler);
            
            // Send savable life-cycle if needed.
 if (mustSendLifeCycleEvent && listener != null)
                listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_OPEN, getSaveables(), false));
            
        }
        
    }

    /**
     * Returns this editor's viewer's undo manager undo context.
     *
     * @return the undo context or <code>null</code> if not available
     * @since 3.1
     */
    private IUndoContext getUndoContext() {
        if (fSourceViewer instanceof ITextViewerExtension6) {
            IUndoManager undoManager= ((ITextViewerExtension6)fSourceViewer).getUndoManager();
            if (undoManager instanceof IUndoManagerExtension)
                return ((IUndoManagerExtension)undoManager).getUndoContext();
        }
        return null;
    }
    
    /*
     * @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
     * @since 3.2
     */
    protected final void setInputWithNotify(IEditorInput input) {
        try {

            doSetInput(input);

            /*
             * The following bugs explain why we fire this property change:
             * https://bugs.eclipse.org/bugs/show_bug.cgi?id=90283
             * https://bugs.eclipse.org/bugs/show_bug.cgi?id=92049
             * https://bugs.eclipse.org/bugs/show_bug.cgi?id=92286
             */
            firePropertyChange(IEditorPart.PROP_INPUT);

        } catch (CoreException x) {
            String title= EditorMessages.Editor_error_setinput_title;
            String msg= EditorMessages.Editor_error_setinput_message;
            Shell shell= getSite().getShell();
            ErrorDialog.openError(shell, title, msg, x.getStatus());
        }
    }
    
    /*
     * @see EditorPart#setInput(org.eclipse.ui.IEditorInput)
     */
    public final void setInput(IEditorInput input) {
        setInputWithNotify(input);
    }
    
    /*
     * @see ITextEditor#close
     */
    public void close(final boolean save) {

        enableSanityChecking(false);

        Display display= getSite().getShell().getDisplay();
        display.asyncExec(new Runnable () {
            public void run() {
                if (fSourceViewer != null)
                    getSite().getPage().closeEditor(AbstractTextEditor.this, save);
            }
        });
    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>IWorkbenchPart</code> method may be extended by subclasses.
     * Subclasses must call <code>super.dispose()</code>.
     * <p>
     * Note that many methods may return <code>null</code> after the editor is
     * disposed.
     * </p>
     */
    public void dispose() {

        if (fActivationListener != null) {
            fActivationListener.dispose();
            fActivationListener= null;
        }

        if (fTitleImage != null) {
            fTitleImage.dispose();
            fTitleImage= null;
        }

        if (fFont != null) {
            fFont.dispose();
            fFont= null;
        }

        disposeNonDefaultCaret();
        fInitialCaret= null;

        if (fForegroundColor != null) {
            fForegroundColor.dispose();
            fForegroundColor= null;
        }

        if (fBackgroundColor != null) {
            fBackgroundColor.dispose();
            fBackgroundColor= null;
        }

        if (fSelectionForegroundColor != null) {
            fSelectionForegroundColor.dispose();
            fSelectionForegroundColor= null;
        }

        if (fSelectionBackgroundColor != null) {
            fSelectionBackgroundColor.dispose();
            fSelectionBackgroundColor= null;
        }

        if (fFindScopeHighlightColor != null) {
            fFindScopeHighlightColor.dispose();
            fFindScopeHighlightColor= null;
        }

        if (fFontPropertyChangeListener != null) {
            JFaceResources.getFontRegistry().removeListener(fFontPropertyChangeListener);
            fFontPropertyChangeListener= null;
        }

        if (fPropertyChangeListener != null) {
            if (fPreferenceStore != null) {
                fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
                fPreferenceStore= null;
            }
            fPropertyChangeListener= null;
        }

        if (fActivationCodeTrigger != null) {
            fActivationCodeTrigger.uninstall();
            fActivationCodeTrigger= null;
        }

        if (fSelectionListener != null) {
            fSelectionListener.uninstall(getSelectionProvider());
            fSelectionListener= null;
        }

        if (fSavable != null) {
            fSavable.disconnectEditor();
            fSavable= null;
        }
        
        disposeDocumentProvider();

        if (fSourceViewer != null) {

            if (fTextListener != null) {
                fSourceViewer.removeTextListener(fTextListener);
                fSourceViewer.removeTextInputListener(fTextListener);
                fTextListener= null;
            }

            uninstallTabsToSpacesConverter();

            fTextInputListener= null;
            fSelectionProvider= null;
            fSourceViewer= null;
        }

        if (fTextContextMenu != null) {
            fTextContextMenu.dispose();
            fTextContextMenu= null;
        }

        if (fRulerContextMenu != null) {
            fRulerContextMenu.dispose();
            fRulerContextMenu= null;
        }

        if (fActions != null) {
            fActions.clear();
            fActions= null;
        }

        if (fSelectionActions != null) {
            fSelectionActions.clear();
            fSelectionActions= null;
        }

        if (fContentActions != null) {
            fContentActions.clear();
            fContentActions= null;
        }

        if (fPropertyActions != null) {
            fPropertyActions.clear();
            fPropertyActions= null;
        }

        if (fStateActions != null) {
            fStateActions.clear();
            fStateActions= null;
        }

        if (fActivationCodes != null) {
            fActivationCodes.clear();
            fActivationCodes= null;
        }

        if (fEditorStatusLine != null)
            fEditorStatusLine= null;

        if (fConfiguration != null)
            fConfiguration= null;

        if (fColumnSupport != null) {
            fColumnSupport.dispose();
            fColumnSupport= null;
        }

        if (fVerticalRuler != null)
            fVerticalRuler= null;

        IOperationHistory history= OperationHistoryFactory.getOperationHistory();
        if (history != null) {
            if (fNonLocalOperationApprover != null)
                history.removeOperationApprover(fNonLocalOperationApprover);
            if (fLinearUndoViolationApprover != null)
                history.removeOperationApprover(fLinearUndoViolationApprover);
        }
        fNonLocalOperationApprover= null;
        fLinearUndoViolationApprover= null;
        
        super.dispose();
    }

    /**
     * Disposes of the connection with the document provider. Subclasses
     * may extend.
     *
     * @since 3.0
     */
    protected void disposeDocumentProvider() {
        IDocumentProvider provider= getDocumentProvider();
        if (provider != null) {

            IEditorInput input= getEditorInput();
            if (input != null)
                provider.disconnect(input);

            if (fElementStateListener != null) {
                provider.removeElementStateListener(fElementStateListener);
                fElementStateListener= null;
            }

        }
        fExplicitDocumentProvider= null;
    }

    /**
     * Determines whether the given preference change affects the editor's
     * presentation. This implementation always returns <code>false</code>.
     * May be reimplemented by subclasses.
     *
     * @param event the event which should be investigated
     * @return <code>true</code> if the event describes a preference change affecting the editor's presentation
     * @since 2.0
     */
    protected boolean affectsTextPresentation(PropertyChangeEvent event) {
        return false;
    }

    /**
     * Returns the symbolic font name for this editor as defined in XML.
     *
     * @return a String with the symbolic font name or <code>null</code> if
     * none is defined
     * @since 2.1
     */
    private String getSymbolicFontName() {
        if (getConfigurationElement() != null)
            return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$
 return null;
    }

    /**
     * Returns the property preference key for the editor font. Subclasses may
     * replace this method.
     *
     * @return a String with the key
     * @since 2.1
     */
    protected final String getFontPropertyPreferenceKey() {
        String symbolicFontName= getSymbolicFontName();
        if (symbolicFontName != null)
            return symbolicFontName;
        return JFaceResources.TEXT_FONT;
    }

    /**
     * Handles a property change event describing a change of the editor's
     * preference store and updates the preference related editor properties.
     * <p>
     * Subclasses may extend.
     * </p>
     *
     * @param event the property change event
     */
    protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {

        if (fSourceViewer == null)
            return;

        String property= event.getProperty();

        if (getFontPropertyPreferenceKey().equals(property))
            // There is a separate handler for font preference changes
 return;

        if (PREFERENCE_COLOR_FOREGROUND.equals(property) || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
                PREFERENCE_COLOR_BACKGROUND.equals(property) || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property) ||
                PREFERENCE_COLOR_SELECTION_FOREGROUND.equals(property) || PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
                PREFERENCE_COLOR_SELECTION_BACKGROUND.equals(property) || PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT.equals(property))
        {
            initializeViewerColors(fSourceViewer);
        } else if (PREFERENCE_COLOR_FIND_SCOPE.equals(property)) {
            initializeFindScopeColor(fSourceViewer);
        } else if (PREFERENCE_USE_CUSTOM_CARETS.equals(property)) {
            updateCaret();
        } else if (PREFERENCE_WIDE_CARET.equals(property)) {
            updateCaret();
        }

        if (affectsTextPresentation(event))
            fSourceViewer.invalidateTextPresentation();

        if (PREFERENCE_HYPERLINKS_ENABLED.equals(property)) {
            if (fSourceViewer instanceof ITextViewerExtension6) {
                IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
                int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
                ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
                textViewer6.setHyperlinkDetectors(detectors, stateMask);
            }
            return;
        }

        if (PREFERENCE_HYPERLINK_KEY_MODIFIER.equals(property)) {
            if (fSourceViewer instanceof ITextViewerExtension6) {
                ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
                IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
                int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
                textViewer6.setHyperlinkDetectors(detectors, stateMask);
            }
            return;
        }
        
        if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
            String [] difference= StringSetSerializer.getDifference((String ) event.getOldValue(), (String ) event.getNewValue());
            IColumnSupport support= (IColumnSupport) getAdapter(IColumnSupport.class);
            for (int i= 0; i < difference.length; i++) {
                RulerColumnDescriptor desc= RulerColumnRegistry.getDefault().getColumnDescriptor(difference[i]);
                if (desc != null && support.isColumnSupported(desc)) {
                    boolean newState= !support.isColumnVisible(desc);
                    support.setColumnVisible(desc, newState);
                }
            }
            return;
        }
        
        if (PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property)) {
            IAction action= getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
            if (action instanceof IUpdate)
                ((IUpdate)action).update();
            return;
        }
        
        if (PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED.equals(property)) {
            IPreferenceStore store= getPreferenceStore();
            if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
                installTextDragAndDrop(getSourceViewer());
            else
                uninstallTextDragAndDrop(getSourceViewer());
            return;
        }
        
    }

    /**
     * Returns the progress monitor related to this editor. It should not be
     * necessary to extend this method.
     *
     * @return the progress monitor related to this editor
     * @since 2.1
     */
    protected IProgressMonitor getProgressMonitor() {

        IProgressMonitor pm= null;

        IStatusLineManager manager= getStatusLineManager();
        if (manager != null)
            pm= manager.getProgressMonitor();

        return pm != null ? pm : new NullProgressMonitor();
    }

    /**
     * Handles an external change of the editor's input element. Subclasses may
     * extend.
     */
    protected void handleEditorInputChanged() {

        String title;
        String msg;
        Shell shell= getSite().getShell();

        final IDocumentProvider provider= getDocumentProvider();
        if (provider == null) {
            // fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066
 close(false);
            return;
        }

        final IEditorInput input= getEditorInput();
        if (provider.isDeleted(input)) {

            if (isSaveAsAllowed()) {

                title= EditorMessages.Editor_error_activated_deleted_save_title;
                msg= EditorMessages.Editor_error_activated_deleted_save_message;

                String [] buttons= {
                        EditorMessages.Editor_error_activated_deleted_save_button_save,
                        EditorMessages.Editor_error_activated_deleted_save_button_close,
                };

                MessageDialog dialog= new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, buttons, 0);

                if (dialog.open() == 0) {
                    IProgressMonitor pm= getProgressMonitor();
                    performSaveAs(pm);
                    if (pm.isCanceled())
                        handleEditorInputChanged();
                } else {
                    close(false);
                }

            } else {

                title= EditorMessages.Editor_error_activated_deleted_close_title;
                msg= EditorMessages.Editor_error_activated_deleted_close_message;
                if (MessageDialog.openConfirm(shell, title, msg))
                    close(false);
            }

        } else {

            title= EditorMessages.Editor_error_activated_outofsync_title;
            msg= EditorMessages.Editor_error_activated_outofsync_message;

            if (MessageDialog.openQuestion(shell, title, msg)) {


                try {
                    if (provider instanceof IDocumentProviderExtension) {
                        IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
                        extension.synchronize(input);
                    } else {
                        doSetInput(input);
                    }
                } catch (CoreException x) {
                    IStatus status= x.getStatus();
                    if (status == null || status.getSeverity() != IStatus.CANCEL) {
                        title= EditorMessages.Editor_error_refresh_outofsync_title;
                        msg= EditorMessages.Editor_error_refresh_outofsync_message;
                        ErrorDialog.openError(shell, title, msg, x.getStatus());
                    }
                }
            }
        }
    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>IEditorPart</code> method calls <code>performSaveAs</code>.
     * Subclasses may reimplement.
     */
    public void doSaveAs() {
        /*
         * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
         * Changed Behavior to make sure that if called inside a regular save (because
         * of deletion of input element) there is a way to report back to the caller.
         */
        performSaveAs(getProgressMonitor());
    }

    /**
     * Performs a save as and reports the result state back to the
     * given progress monitor. This default implementation does nothing.
     * Subclasses may reimplement.
     *
     * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
     */
    protected void performSaveAs(IProgressMonitor progressMonitor) {
    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>IEditorPart</code> method may be extended by subclasses.
     *
     * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
     */
    public void doSave(IProgressMonitor progressMonitor) {

        IDocumentProvider p= getDocumentProvider();
        if (p == null)
            return;

        if (p.isDeleted(getEditorInput())) {

            if (isSaveAsAllowed()) {

                /*
                 * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
                 * Changed Behavior to make sure that if called inside a regular save (because
                 * of deletion of input element) there is a way to report back to the caller.
                 */
                performSaveAs(progressMonitor);

            } else {

                Shell shell= getSite().getShell();
                String title= EditorMessages.Editor_error_save_deleted_title;
                String msg= EditorMessages.Editor_error_save_deleted_message;
                MessageDialog.openError(shell, title, msg);
            }

        } else {
            updateState(getEditorInput());
            validateState(getEditorInput());
            performSave(false, progressMonitor);
        }
    }

    /**
     * Enables/disables sanity checking.
     * @param enable <code>true</code> if sanity checking should be enabled, <code>false</code> otherwise
     * @since 2.0
     */
    protected void enableSanityChecking(boolean enable) {
        synchronized (this) {
            fIsSanityCheckEnabled= enable;
        }
    }

    /**
     * Checks the state of the given editor input if sanity checking is enabled.
     * @param input the editor input whose state is to be checked
     * @since 2.0
     */
    protected void safelySanityCheckState(IEditorInput input) {
        boolean enabled= false;

        synchronized (this) {
            enabled= fIsSanityCheckEnabled;
        }

        if (enabled)
            sanityCheckState(input);
    }

    /**
     * Checks the state of the given editor input.
     * @param input the editor input whose state is to be checked
     * @since 2.0
     */
    protected void sanityCheckState(IEditorInput input) {

        IDocumentProvider p= getDocumentProvider();
        if (p == null)
            return;

        if (p instanceof IDocumentProviderExtension3) {

            IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;

            long stamp= p.getModificationStamp(input);
            if (stamp != fModificationStamp) {
                fModificationStamp= stamp;
                if (!p3.isSynchronized(input))
                    handleEditorInputChanged();
            }

        } else {

            if (fModificationStamp == -1)
                fModificationStamp= p.getSynchronizationStamp(input);

            long stamp= p.getModificationStamp(input);
            if (stamp != fModificationStamp) {
                fModificationStamp= stamp;
                if (stamp != p.getSynchronizationStamp(input))
                    handleEditorInputChanged();
            }
        }

        updateState(getEditorInput());
        updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
    }

    /**
     * Enables/disables state validation.
     * @param enable <code>true</code> if state validation should be enabled, <code>false</code> otherwise
     * @since 2.1
     */
    protected void enableStateValidation(boolean enable) {
        synchronized (this) {
            fIsStateValidationEnabled= enable;
        }
    }

    /**
     * Validates the state of the given editor input. The predominate intent
     * of this method is to take any action probably necessary to ensure that
     * the input can persistently be changed.
     *
     * @param input the input to be validated
     * @since 2.0
     */
    protected void validateState(IEditorInput input) {

        IDocumentProvider provider= getDocumentProvider();
        if (! (provider instanceof IDocumentProviderExtension))
            return;

        IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;

        try {

            extension.validateState(input, getSite().getShell());

        } catch (CoreException x) {
            IStatus status= x.getStatus();
            if (status == null || status.getSeverity() != IStatus.CANCEL) {
                Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
                ILog log= Platform.getLog(bundle);
                log.log(x.getStatus());

                Shell shell= getSite().getShell();
                String title= EditorMessages.Editor_error_validateEdit_title;
                String msg= EditorMessages.Editor_error_validateEdit_message;
                ErrorDialog.openError(shell, title, msg, x.getStatus());
            }
            return;
        }

        if (fSourceViewer != null)
            fSourceViewer.setEditable(isEditable());

        updateStateDependentActions();
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension2#validateEditorInputState()
     * @since 2.1
     */
    public boolean validateEditorInputState() {

        boolean enabled= false;

        synchronized (this) {
            enabled= fIsStateValidationEnabled;
        }

        if (enabled) {

            ISourceViewer viewer= fSourceViewer;
            if (viewer == null)
                return false;

            fTextInputListener.inputChanged= false;
            viewer.addTextInputListener(fTextInputListener);

            try {
                final IEditorInput input= getEditorInput();
                BusyIndicator.showWhile(getSite().getShell().getDisplay(), new Runnable () {
                    /*
                     * @see java.lang.Runnable#run()
                     */
                    public void run() {
                        validateState(input);
                    }
                });
                sanityCheckState(input);
                return !isEditorInputReadOnly() && !fTextInputListener.inputChanged;

            } finally {
                viewer.removeTextInputListener(fTextInputListener);
            }

        }

        return !isEditorInputReadOnly();
    }

    /**
     * Updates the state of the given editor input such as read-only flag.
     *
     * @param input the input to be validated
     * @since 2.0
     */
    protected void updateState(IEditorInput input) {
        IDocumentProvider provider= getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension) {
            IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
            try {

                boolean wasReadOnly= isEditorInputReadOnly();
                extension.updateStateCache(input);

                if (fSourceViewer != null)
                    fSourceViewer.setEditable(isEditable());

                if (wasReadOnly != isEditorInputReadOnly())
                    updateStateDependentActions();

            } catch (CoreException x) {
                Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
                ILog log= Platform.getLog(bundle);
                log.log(x.getStatus());
            }
        }
    }

    /**
     * Performs the save and handles errors appropriately.
     *
     * @param overwrite indicates whether or not overwriting is allowed
     * @param progressMonitor the monitor in which to run the operation
     * @since 3.0
     */
    protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {

        IDocumentProvider provider= getDocumentProvider();
        if (provider == null)
            return;

        try {

            provider.aboutToChange(getEditorInput());
            IEditorInput input= getEditorInput();
            provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
            editorSaved();

        } catch (CoreException x) {
            IStatus status= x.getStatus();
            if (status == null || status.getSeverity() != IStatus.CANCEL)
                handleExceptionOnSave(x, progressMonitor);
        } finally {
            provider.changed(getEditorInput());
        }
    }

    /**
     * Handles the given exception. If the exception reports an out-of-sync
     * situation, this is reported to the user. Otherwise, the exception
     * is generically reported.
     *
     * @param exception the exception to handle
     * @param progressMonitor the progress monitor
     */
    protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {

        try {
            ++ fErrorCorrectionOnSave;

            boolean isSynchronized= false;
            IDocumentProvider p= getDocumentProvider();

            if (p instanceof IDocumentProviderExtension3) {
                IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
                isSynchronized= p3.isSynchronized(getEditorInput());
            } else {
                long modifiedStamp= p.getModificationStamp(getEditorInput());
                long synchStamp= p.getSynchronizationStamp(getEditorInput());
                isSynchronized= (modifiedStamp == synchStamp);
            }

            if (isNotSynchronizedException(exception) && fErrorCorrectionOnSave == 1 && !isSynchronized) {
                String title= EditorMessages.Editor_error_save_outofsync_title;
                String msg= NLSUtility.format(EditorMessages.Editor_error_save_outofsync_message, getEditorInput().getToolTipText());

                if (MessageDialog.openQuestion(getSite().getShell(), title, msg))
                    performSave(true, progressMonitor);
                else {
                    /*
                     * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
                     * Set progress monitor to canceled in order to report back
                     * to enclosing operations.
                     */
                    if (progressMonitor != null)
                        progressMonitor.setCanceled(true);
                }
            } else {
                String title= EditorMessages.Editor_error_save_title;
                String msg= EditorMessages.Editor_error_save_message;
                openSaveErrorDialog(title, msg, exception);

                /*
                 * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
                 * Set progress monitor to canceled in order to report back
                 * to enclosing operations.
                 */
                if (progressMonitor != null)
                    progressMonitor.setCanceled(true);
            }
        } finally {
            -- fErrorCorrectionOnSave;
        }
    }

    /**
     * Presents an error dialog to the user when a problem
     * happens during save.
     * <p>
     * Subclasses can decide to override the given title and message.
     * </p>
     *
     * @param title the dialog title
     * @param message the message to display
     * @param exception the exception to handle
     * @since 3.3
     */
    protected void openSaveErrorDialog(String title, String message, CoreException exception) {
        ErrorDialog.openError(getSite().getShell(), title, message, exception.getStatus());
    }

    /**
     * Tells whether the given core exception is exactly the
     * exception which is thrown for a non-synchronized element.
     *
     * @param ex the core exception
     * @return <code>true</code> iff the given core exception is exactly the
     * exception which is thrown for a non-synchronized element
     * @since 3.1
     */
    private boolean isNotSynchronizedException(CoreException ex) {
        IDocumentProvider provider= getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension5)
            return ((IDocumentProviderExtension5)provider).isNotSynchronizedException(getEditorInput(), ex);
        return false;
    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>IEditorPart</code> method returns <code>false</code>.
     * Subclasses may override.
     *
     * @return <code>false</code>
     */
    public boolean isSaveAsAllowed() {
        return false;
    }

    /*
     * @see EditorPart#isDirty()
     */
    public boolean isDirty() {
        IDocumentProvider p= getDocumentProvider();
        return p == null ? false : p.canSaveDocument(getEditorInput());
    }

    /**
     * The <code>AbstractTextEditor</code> implementation of this
     * <code>ITextEditor</code> method may be extended by subclasses.
     */
    public void doRevertToSaved() {
        IDocumentProvider p= getDocumentProvider();
        if (p == null)
            return;

        performRevert();
    }

    /**
     * Performs revert and handles errors appropriately.
     * <p>
     * Subclasses may extend.
     * </p>
     *
     * @since 3.0
     */
    protected void performRevert() {

        IDocumentProvider provider= getDocumentProvider();
        if (provider == null)
            return;

        try {

            provider.aboutToChange(getEditorInput());
            provider.resetDocument(getEditorInput());
            editorSaved();

        } catch (CoreException x) {
            IStatus status= x.getStatus();
            if (status == null || status.getSeverity() != IStatus.CANCEL ) {
                Shell shell= getSite().getShell();
                String title= EditorMessages.Editor_error_revert_title;
                String msg= EditorMessages.Editor_error_revert_message;
                ErrorDialog.openError(shell, title, msg, x.getStatus());
            }
        } finally {
            provider.changed(getEditorInput());
        }
    }

    /**
     * Performs any additional action necessary to perform after the input
     * document's content has been replaced.
     * <p>
     * Clients may extended this method.
     *
     * @since 3.0
     */
    protected void handleElementContentReplaced() {
    }

    /*
     * @see ITextEditor#setAction(String, IAction)
     */
    public void setAction(String actionID, IAction action) {
        Assert.isNotNull(actionID);
        if (action == null) {
            action= (IAction) fActions.remove(actionID);
            if (action != null)
                fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
        } else {
            fActions.put(actionID, action);
            fActivationCodeTrigger.registerActionForKeyActivation(action);
        }
    }

    /*
     * @see ITextEditor#setActionActivationCode(String, char, int, int)
     */
    public void setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask) {

        Assert.isNotNull(actionID);

        ActionActivationCode found= findActionActivationCode(actionID);
        if (found == null) {
            found= new ActionActivationCode(actionID);
            fActivationCodes.add(found);
        }

        found.fCharacter= activationCharacter;
        found.fKeyCode= activationKeyCode;
        found.fStateMask= activationStateMask;
    }

    /**
     * Returns the activation code registered for the specified action.
     *
     * @param actionID the action id
     * @return the registered activation code or <code>null</code> if no code has been installed
     */
    private ActionActivationCode findActionActivationCode(String actionID) {
        int size= fActivationCodes.size();
        for (int i= 0; i < size; i++) {
            ActionActivationCode code= (ActionActivationCode) fActivationCodes.get(i);
            if (actionID.equals(code.fActionId))
                return code;
        }
        return null;
    }

    /*
     * @see ITextEditor#removeActionActivationCode(String)
     */
    public void removeActionActivationCode(String actionID) {
        Assert.isNotNull(actionID);
        ActionActivationCode code= findActionActivationCode(actionID);
        if (code != null)
            fActivationCodes.remove(code);
    }

    /*
     * @see ITextEditor#getAction(String)
     */
    public IAction getAction(String actionID) {
        Assert.isNotNull(actionID);
        IAction action= (IAction) fActions.get(actionID);

        if (action == null) {
            action= findContributedAction(actionID);
            if (action != null)
                setAction(actionID, action);
        }

        return action;
    }

    /**
     * Returns the action with the given action id that has been contributed via XML to this editor.
     * The lookup honors the dependencies of plug-ins.
     *
     * @param actionID the action id to look up
     * @return the action that has been contributed
     * @since 2.0
     */
    private IAction findContributedAction(String actionID) {
        List actions= new ArrayList ();
        IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$
 for (int i= 0; i < elements.length; i++) {
            IConfigurationElement element= elements[i];
            if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) {
                if (!getSite().getId().equals(element.getAttribute("targetID"))) //$NON-NLS-1$
 continue;

                IConfigurationElement[] children= element.getChildren("action"); //$NON-NLS-1$
 for (int j= 0; j < children.length; j++) {
                    IConfigurationElement child= children[j];
                    if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$
 actions.add(child);
                }
            }
        }
        int actionSize= actions.size();
        if (actionSize > 0) {
            IConfigurationElement element;
            if (actionSize > 1) {
                IConfigurationElement[] actionArray= (IConfigurationElement[])actions.toArray(new IConfigurationElement[actionSize]);
                ConfigurationElementSorter sorter= new ConfigurationElementSorter() {
                    /*
                     * @see org.eclipse.ui.texteditor.ConfigurationElementSorter#getConfigurationElement(java.lang.Object)
                     */
                    public IConfigurationElement getConfigurationElement(Object object) {
                        return (IConfigurationElement)object;
                    }
                };
                sorter.sort(actionArray);
                element= actionArray[0];
            } else
                element= (IConfigurationElement)actions.get(0);

            try {
                return new ContributedAction(getSite(), element);
            } catch (CommandNotMappedException e) {
                // out of luck, no command action mapping
 }
        }

        return null;
    }

    /**
     * Updates the specified action by calling <code>IUpdate.update</code>
     * if applicable.
     *
     * @param actionId the action id
     */
    private void updateAction(String actionId) {
        Assert.isNotNull(actionId);
        if (fActions != null) {
            IAction action= (IAction) fActions.get(actionId);
            if (action instanceof IUpdate)
                ((IUpdate) action).update();
        }
    }

    /**
     * Marks or unmarks the given action to be updated on text selection changes.
     *
     * @param actionId the action id
     * @param mark <code>true</code> if the action is selection dependent
     */
    public void markAsSelectionDependentAction(String actionId, boolean mark) {
        Assert.isNotNull(actionId);
        if (mark) {
            if (!fSelectionActions.contains(actionId))
                fSelectionActions.add(actionId);
        } else
            fSelectionActions.remove(actionId);
    }

    /**
     * Marks or unmarks the given action to be updated on content changes.
     *
     * @param actionId the action id
     * @param mark <code>true</code> if the action is content dependent
     */
    public void markAsContentDependentAction(String actionId, boolean mark) {
        Assert.isNotNull(actionId);
        if (mark) {
            if (!fContentActions.contains(actionId))
                fContentActions.add(actionId);
        } else
            fContentActions.remove(actionId);
    }

    /**
     * Marks or unmarks the given action to be updated on property changes.
     *
     * @param actionId the action id
     * @param mark <code>true</code> if the action is property dependent
     * @since 2.0
     */
    public void markAsPropertyDependentAction(String actionId, boolean mark) {
        Assert.isNotNull(actionId);
        if (mark) {
            if (!fPropertyActions.contains(actionId))
                fPropertyActions.add(actionId);
        } else
            fPropertyActions.remove(actionId);
    }

    /**
     * Marks or unmarks the given action to be updated on state changes.
     *
     * @param actionId the action id
     * @param mark <code>true</code> if the action is state dependent
     * @since 2.0
     */
    public void markAsStateDependentAction(String actionId, boolean mark) {
        Assert.isNotNull(actionId);
        if (mark) {
            if (!fStateActions.contains(actionId))
                fStateActions.add(actionId);
        } else
            fStateActions.remove(actionId);
    }

    /**
     * Updates all selection dependent actions.
     */
    protected void updateSelectionDependentActions() {
        if (fSelectionActions != null) {
            Iterator e= fSelectionActions.iterator();
            while (e.hasNext())
                updateAction((String ) e.next());
        }
    }

    /**
     * Updates all content dependent actions.
     */
    protected void updateContentDependentActions() {
        if (fContentActions != null) {
            Iterator e= fContentActions.iterator();
            while (e.hasNext())
                updateAction((String ) e.next());
        }
    }

    /**
     * Updates all property dependent actions.
     * @since 2.0
     */
    protected void updatePropertyDependentActions() {
        if (fPropertyActions != null) {
            Iterator e= fPropertyActions.iterator();
            while (e.hasNext())
                updateAction((String ) e.next());
        }
    }

    /**
     * Updates all state dependent actions.
     * @since 2.0
     */
    protected void updateStateDependentActions() {
        if (fStateActions != null) {
            Iterator e= fStateActions.iterator();
            while (e.hasNext())
                updateAction((String ) e.next());
        }
    }

    /**
     * Creates action entries for all SWT StyledText actions as defined in
     * <code>org.eclipse.swt.custom.ST</code>. Overwrites and
     * extends the list of these actions afterwards.
     * <p>
     * Subclasses may extend.
     * </p>
     * @since 2.0
     */
    protected void createNavigationActions() {

        IAction action;

        StyledText textWidget= fSourceViewer.getTextWidget();
        for (int i= 0; i < ACTION_MAP.length; i++) {
            IdMapEntry entry= ACTION_MAP[i];
            action= new TextNavigationAction(textWidget, entry.getAction());
            action.setActionDefinitionId(entry.getActionId());
            setAction(entry.getActionId(), action);
        }

        action= new ToggleOverwriteModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleOverwriteMode."); //$NON-NLS-1$
 action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE);
        setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, action);
        textWidget.setKeyBinding(SWT.INSERT, SWT.NULL);

        action= new ScrollLinesAction(-1);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP);
        setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action);

        action= new ScrollLinesAction(1);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN);
        setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN, action);

        action= new LineEndAction(textWidget, false);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END);
        setAction(ITextEditorActionDefinitionIds.LINE_END, action);

        action= new LineStartAction(textWidget, false);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
        setAction(ITextEditorActionDefinitionIds.LINE_START, action);

        action= new LineEndAction(textWidget, true);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END);
        setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END, action);

        action= new LineStartAction(textWidget, true);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
        setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action);

        // to accommodate https://bugs.eclipse.org/bugs/show_bug.cgi?id=51516
 // nullify handling of DELETE key by StyledText
 textWidget.setKeyBinding(SWT.DEL, SWT.NULL);
    }

    /**
     * Creates this editor's accessibility actions.
     * @since 2.0
     */
    private void createAccessibilityActions() {
        IAction action= new ShowRulerContextMenuAction();
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU);
        setAction(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU, action);
    }

    /**
     * Creates this editor's undo/redo actions.
     * <p>
     * Subclasses may override or extend.</p>
     *
     * @since 3.1
     */
    protected void createUndoRedoActions() {
        IUndoContext undoContext= getUndoContext();
        if (undoContext != null) {
            // Use actions provided by global undo/redo

            // Create the undo action
 OperationHistoryActionHandler undoAction= new UndoActionHandler(getEditorSite(), undoContext);
            PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);
            undoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);
            registerUndoRedoAction(ITextEditorActionConstants.UNDO, undoAction);

            // Create the redo action.
 OperationHistoryActionHandler redoAction= new RedoActionHandler(getEditorSite(), undoContext);
            PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION);
            redoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.REDO);
            registerUndoRedoAction(ITextEditorActionConstants.REDO, redoAction);

            // Install operation approvers
 IOperationHistory history= OperationHistoryFactory.getOperationHistory();

            // The first approver will prompt when operations affecting outside elements are to be undone or redone.
 if (fNonLocalOperationApprover != null)
                history.removeOperationApprover(fNonLocalOperationApprover);
            fNonLocalOperationApprover= getUndoRedoOperationApprover(undoContext);
            history.addOperationApprover(fNonLocalOperationApprover);

            // The second approver will prompt from this editor when an undo is attempted on an operation
 // and it is not the most recent operation in the editor.
 if (fLinearUndoViolationApprover != null)
                history.removeOperationApprover(fLinearUndoViolationApprover);
            fLinearUndoViolationApprover= new LinearUndoViolationUserApprover(undoContext, this);
            history.addOperationApprover(fLinearUndoViolationApprover);

        } else {
            // Use text operation actions (pre 3.1 style)

            ResourceAction action;
            
            if (getAction(ITextEditorActionConstants.UNDO) == null) {
                action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Undo.", this, ITextOperationTarget.UNDO); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION);
                action.setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);
                setAction(ITextEditorActionConstants.UNDO, action);
            }

            if (getAction(ITextEditorActionConstants.REDO) == null) {
                action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Redo.", this, ITextOperationTarget.REDO); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION);
                action.setActionDefinitionId(IWorkbenchActionDefinitionIds.REDO);
                setAction(ITextEditorActionConstants.REDO, action);
            }
        }
    }
    
    /**
     * Registers the given undo/redo action under the given ID and
     * ensures that previously installed actions get disposed. It
     * also takes care of re-registering the new action with the
     * global action handler.
     *
     * @param actionId the action id under which to register the action
     * @param action the action to register
     * @since 3.1
     */
    private void registerUndoRedoAction(String actionId, OperationHistoryActionHandler action) {
        IAction oldAction= getAction(actionId);
        if (oldAction instanceof OperationHistoryActionHandler)
            ((OperationHistoryActionHandler)oldAction).dispose();

        setAction(actionId, action);
        
        IActionBars actionBars= getEditorSite().getActionBars();
        if (actionBars != null)
            actionBars.setGlobalActionHandler(actionId, action);
    }

    /**
     * Return an {@link IOperationApprover} appropriate for approving the undo and
     * redo of operations that have the specified undo context.
     * <p>
     * Subclasses may override.
     * </p>
     * @param undoContext the IUndoContext of operations that should be examined
     * by the operation approver
     * @return the <code>IOperationApprover</code> appropriate for approving undo
     * and redo operations inside this editor, or <code>null</code> if no
     * approval is needed
     * @since 3.1
     */
    protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
        return new NonLocalUndoUserApprover(undoContext, this, new Object [] { getEditorInput() }, Object .class);
    }

    /**
     * Creates this editor's standard actions and connects them with the global
     * workbench actions.
     * <p>
     * Subclasses may extend.</p>
     */
    protected void createActions() {

        ResourceAction action;

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.CUT);
        setAction(ITextEditorActionConstants.CUT, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY);
        setAction(ITextEditorActionConstants.COPY, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.PASTE);
        setAction(ITextEditorActionConstants.PASTE, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Delete.", this, ITextOperationTarget.DELETE); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.DELETE);
        setAction(ITextEditorActionConstants.DELETE, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLine.", this, DeleteLineAction.WHOLE, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE);
        setAction(ITextEditorActionConstants.DELETE_LINE, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLine.", this, DeleteLineAction.WHOLE, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE);
        setAction(ITextEditorActionConstants.CUT_LINE, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING);
        setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_BEGINNING_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING);
        setAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToEnd.", this, DeleteLineAction.TO_END, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END);
        setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action);

        action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToEnd.", this, DeleteLineAction.TO_END, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_END_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_END);
        setAction(ITextEditorActionConstants.CUT_LINE_TO_END, action);

        action= new JoinLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.JoinLines.", this, " "); //$NON-NLS-1$ //$NON-NLS-2$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.JOIN_LINES_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.JOIN_LINES);
        setAction(ITextEditorActionConstants.JOIN_LINES, action);

        action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SetMark.", this, MarkAction.SET_MARK); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK);
        setAction(ITextEditorActionConstants.SET_MARK, action);

        action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ClearMark.", this, MarkAction.CLEAR_MARK); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK);
        setAction(ITextEditorActionConstants.CLEAR_MARK, action);

        action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SwapMark.", this, MarkAction.SWAP_MARK); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK);
        setAction(ITextEditorActionConstants.SWAP_MARK, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SelectAll.", this, ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.SELECT_ALL);
        setAction(ITextEditorActionConstants.SELECT_ALL, action);

        action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT);
        setAction(ITextEditorActionConstants.SHIFT_RIGHT, action);

        action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT) { //$NON-NLS-1$
 public void update() {
                updateForTab();
            }
        };
        setAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, action);

        action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftLeft.", this, ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT);
        setAction(ITextEditorActionConstants.SHIFT_LEFT, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Print.", this, ITextOperationTarget.PRINT, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.PRINT);
        setAction(ITextEditorActionConstants.PRINT, action);

        action= new FindReplaceAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindReplace.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_REPLACE);
        setAction(ITextEditorActionConstants.FIND, action);

        action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindNext.", this, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_NEXT);
        setAction(ITextEditorActionConstants.FIND_NEXT, action);

        action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindPrevious.", this, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_PREVIOUS);
        setAction(ITextEditorActionConstants.FIND_PREVIOUS, action);

        action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncremental.", this, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL);
        setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action);

        action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncrementalReverse.", this, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_REVERSE_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL_REVERSE);
        setAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, action);

        action= new SaveAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Save.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SAVE_ACTION);
        /*
         * if the line below is uncommented then the key binding does not work any more
         * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=53417
         */
// action.setActionDefinitionId(ITextEditorActionDefinitionIds.SAVE);
 setAction(ITextEditorActionConstants.SAVE, action);

        action= new RevertToSavedAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Revert.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION);
        action.setActionDefinitionId(IWorkbenchActionDefinitionIds.REVERT_TO_SAVED);
        setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action);

        action= new GotoLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.GotoLine.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO);
        setAction(ITextEditorActionConstants.GOTO_LINE, action);

        action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesUp.", this, true, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
        setAction(ITextEditorActionConstants.MOVE_LINE_UP, action);

        action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesDown.", this, false, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
        setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action);

        action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineUp.", this, true, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
        setAction(ITextEditorActionConstants.COPY_LINE_UP, action);

        action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineDown.", this, false, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
        setAction(ITextEditorActionConstants.COPY_LINE_DOWN, action);

        action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.UpperCase.", this, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.UPPER_CASE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.UPPER_CASE);
        setAction(ITextEditorActionConstants.UPPER_CASE, action);

        action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.LowerCase.", this, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.LOWER_CASE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.LOWER_CASE);
        setAction(ITextEditorActionConstants.LOWER_CASE, action);

        action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnter.", this, false); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER);
        setAction(ITextEditorActionConstants.SMART_ENTER, action);

        action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnterInverse.", this, true); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER_INVERSE);
        setAction(ITextEditorActionConstants.SMART_ENTER_INVERSE, action);

        action= new ToggleInsertModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleInsertMode."); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.TOGGLE_INSERT_MODE_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
        setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);

        action= new HippieCompleteAction(EditorMessages.getBundleForConstructedKeys(), "Editor.HippieCompletion.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
        setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);

        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.QuickAssist.", this, ISourceViewer.QUICK_ASSIST); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.QUICK_ASSIST_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
        setAction(ITextEditorActionConstants.QUICK_ASSIST, action);
        markAsStateDependentAction(ITextEditorActionConstants.QUICK_ASSIST, true);

        action= new GotoAnnotationAction(this, true);
        setAction(ITextEditorActionConstants.NEXT, action);
        action= new GotoAnnotationAction(this, false);
        setAction(ITextEditorActionConstants.PREVIOUS, action);

        action= new RecenterAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Recenter.", this); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.RECENTER_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.RECENTER);
        setAction(ITextEditorActionConstants.RECENTER, action);
        
        action= new ShowWhitespaceCharactersAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowWhitespaceCharacters.", this, getPreferenceStore()); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_WHITESPACE_CHARACTERS_ACTION);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
        setAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS, action);
        
        ResourceAction resAction= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", this, ISourceViewer.INFORMATION, true); //$NON-NLS-1$
 resAction= new InformationDispatchAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", (TextOperationAction) resAction); //$NON-NLS-1$
 action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_INFORMATION_ACTION);
        resAction.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION);
        setAction(ITextEditorActionConstants.SHOW_INFORMATION, resAction);
        

        PropertyDialogAction openProperties= new PropertyDialogAction(
                new IShellProvider() {
                    public Shell getShell() {
                        return getSite().getShell();
                    }
                },
                new ISelectionProvider() {
                    public void addSelectionChangedListener(ISelectionChangedListener listener) {
                    }
                    public ISelection getSelection() {
                        return new StructuredSelection(getEditorInput());
                    }
                    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
                    }
                    public void setSelection(ISelection selection) {
                    }
                });
        openProperties.setActionDefinitionId(IWorkbenchActionDefinitionIds.PROPERTIES);
        setAction(ITextEditorActionConstants.PROPERTIES, openProperties);

        markAsContentDependentAction(ITextEditorActionConstants.UNDO, true);
        markAsContentDependentAction(ITextEditorActionConstants.REDO, true);
        markAsContentDependentAction(ITextEditorActionConstants.FIND, true);
        markAsContentDependentAction(ITextEditorActionConstants.FIND_NEXT, true);
        markAsContentDependentAction(ITextEditorActionConstants.FIND_PREVIOUS, true);
        markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL, true);
        markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, true);

        markAsSelectionDependentAction(ITextEditorActionConstants.CUT, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.COPY, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.PASTE, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.DELETE, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.UPPER_CASE, true);
        markAsSelectionDependentAction(ITextEditorActionConstants.LOWER_CASE, true);

        markAsPropertyDependentAction(ITextEditorActionConstants.UNDO, true);
        markAsPropertyDependentAction(ITextEditorActionConstants.REDO, true);
        markAsPropertyDependentAction(ITextEditorActionConstants.REVERT_TO_SAVED, true);

        markAsStateDependentAction(ITextEditorActionConstants.UNDO, true);
        markAsStateDependentAction(ITextEditorActionConstants.REDO, true);
        markAsStateDependentAction(ITextEditorActionConstants.CUT, true);
        markAsStateDependentAction(ITextEditorActionConstants.PASTE, true);
        markAsStateDependentAction(ITextEditorActionConstants.DELETE, true);
        markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
        markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
        markAsStateDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true);
        markAsStateDependentAction(ITextEditorActionConstants.FIND, true);
        markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE, true);
        markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, true);
        markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_END, true);
        markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_UP, true);
        markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_DOWN, true);
        markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE, true);
        markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, true);
        markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_END, true);

        setActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT_TAB,'\t', -1, SWT.NONE);
        setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT, '\t', -1, SWT.SHIFT);
    }

    /**
     * Convenience method to add the action installed under the given action id to the given menu.
     * @param menu the menu to add the action to
     * @param actionId the id of the action to be added
     */
    protected final void addAction(IMenuManager menu, String actionId) {
        IAction action= getAction(actionId);
        if (action != null) {
            if (action instanceof IUpdate)
                ((IUpdate) action).update();
            menu.add(action);
        }
    }

    /**
     * Convenience method to add the action installed under the given action id to the specified group of the menu.
     * @param menu the menu to add the action to
     * @param group the group in the menu
     * @param actionId the id of the action to add
     */
    protected final void addAction(IMenuManager menu, String group, String actionId) {
        IAction action= getAction(actionId);
        if (action != null) {
            if (action instanceof IUpdate)
                ((IUpdate) action).update();

            IMenuManager subMenu= menu.findMenuUsingPath(group);
            if (subMenu != null)
                subMenu.add(action);
            else
                menu.appendToGroup(group, action);
        }
    }

    /**
     * Convenience method to add a new group after the specified group.
     * @param menu the menu to add the new group to
     * @param existingGroup the group after which to insert the new group
     * @param newGroup the new group
     */
    protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) {
        IMenuManager subMenu= menu.findMenuUsingPath(existingGroup);
        if (subMenu != null)
            subMenu.add(new Separator(newGroup));
        else
            menu.appendToGroup(existingGroup, new Separator(newGroup));
    }

    /**
     * Sets up the ruler context menu before it is made visible.
     * <p>
     * Subclasses may extend to add other actions.</p>
     *
     * @param menu the menu
     */
    protected void rulerContextMenuAboutToShow(IMenuManager menu) {

        menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
        menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));

        for (Iterator i= fRulerContextMenuListeners.iterator(); i.hasNext();)
            ((IMenuListener) i.next()).menuAboutToShow(menu);

        addAction(menu, ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS);
        addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS);
    }

    /**
     * Sets up this editor's context menu before it is made visible.
     * <p>
     * Subclasses may extend to add other actions.</p>
     *
     * @param menu the menu
     */
    protected void editorContextMenuAboutToShow(IMenuManager menu) {

        menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
        menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_SAVE));
        menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
        menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT));
        menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
        menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND));
        menu.add(new Separator(IWorkbenchActionConstants.GROUP_ADD));
        menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
        menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));

        if (isEditable()) {
            addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO);
            addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REVERT_TO_SAVED);
            addAction(menu, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE);
            addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT);
            addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
            addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE);
        } else {
            addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
        }
    }

    /**
     * Returns the status line manager of this editor.
     *
     * @return the status line manager of this editor
     * @since 2.0, protected since 3.3
     */
    protected IStatusLineManager getStatusLineManager() {
        return getEditorSite().getActionBars().getStatusLineManager();
    }

    /*
     * @see IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class required) {

        if (IEditorStatusLine.class.equals(required)) {
            if (fEditorStatusLine == null) {
                IStatusLineManager statusLineManager= getStatusLineManager();
                ISelectionProvider selectionProvider= getSelectionProvider();
                if (statusLineManager != null && selectionProvider != null)
                    fEditorStatusLine= new EditorStatusLine(statusLineManager, selectionProvider);
            }
            return fEditorStatusLine;
        }

        if (IVerticalRulerInfo.class.equals(required)) {
            if (fVerticalRuler != null)
                return fVerticalRuler;
        }

        if (IMarkRegionTarget.class.equals(required)) {
            if (fMarkRegionTarget == null) {
                IStatusLineManager manager= getStatusLineManager();
                if (manager != null)
                    fMarkRegionTarget= (fSourceViewer == null ? null : new MarkRegionTarget(fSourceViewer, manager));
            }
            return fMarkRegionTarget;
        }

        if (DeleteLineTarget.class.equals(required)){
            if (fDeleteLineTarget == null) {
                fDeleteLineTarget= new DeleteLineTarget(fSourceViewer);
            }
            return fDeleteLineTarget;
        }

        if (IncrementalFindTarget.class.equals(required)) {
            if (fIncrementalFindTarget == null) {
                IStatusLineManager manager= getStatusLineManager();
                if (manager != null)
                    fIncrementalFindTarget= (fSourceViewer == null ? null : new IncrementalFindTarget(fSourceViewer, manager));
            }
            return fIncrementalFindTarget;
        }

        if (IFindReplaceTarget.class.equals(required)) {
            if (fFindReplaceTarget == null) {
                IFindReplaceTarget target= (fSourceViewer == null ? null : fSourceViewer.getFindReplaceTarget());
                if (target != null) {
                    fFindReplaceTarget= new FindReplaceTarget(this, target);
                    if (fFindScopeHighlightColor != null)
                        fFindReplaceTarget.setScopeHighlightColor(fFindScopeHighlightColor);
                }
            }
            return fFindReplaceTarget;
        }

        if (ITextOperationTarget.class.equals(required))
            return (fSourceViewer == null ? null : fSourceViewer.getTextOperationTarget());

        if (IRewriteTarget.class.equals(required)) {
            if (fSourceViewer instanceof ITextViewerExtension) {
                ITextViewerExtension extension= (ITextViewerExtension) fSourceViewer;
                return extension.getRewriteTarget();
            }
            return null;
        }

        if (Control.class.equals(required))
            return fSourceViewer != null ? fSourceViewer.getTextWidget() : null;

        if (IColumnSupport.class.equals(required)) {
            if (fColumnSupport == null)
                fColumnSupport= createColumnSupport();
            return fColumnSupport;
        }

        return super.getAdapter(required);
    }

    /*
     * @see IWorkbenchPart#setFocus()
     */
    public void setFocus() {
        if (fSourceViewer != null && fSourceViewer.getTextWidget() != null)
            fSourceViewer.getTextWidget().setFocus();
    }

    /*
     * @see ITextEditor#showsHighlightRangeOnly()
     */
    public boolean showsHighlightRangeOnly() {
        return fShowHighlightRangeOnly;
    }

    /*
     * @see ITextEditor#showHighlightRangeOnly(boolean)
     */
    public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
        fShowHighlightRangeOnly= showHighlightRangeOnly;
    }

    /*
     * @see ITextEditor#setHighlightRange(int, int, boolean)
     */
    public void setHighlightRange(int offset, int length, boolean moveCursor) {
        if (fSourceViewer == null)
            return;

        if (fShowHighlightRangeOnly) {
            if (moveCursor)
                fSourceViewer.setVisibleRegion(offset, length);
        } else {
            IRegion rangeIndication= fSourceViewer.getRangeIndication();
            if (rangeIndication == null || offset != rangeIndication.getOffset() || length != rangeIndication.getLength())
                fSourceViewer.setRangeIndication(offset, length, moveCursor);
        }
    }

    /*
     * @see ITextEditor#getHighlightRange()
     */
    public IRegion getHighlightRange() {
        if (fSourceViewer == null)
            return null;

        if (fShowHighlightRangeOnly)
            return getCoverage(fSourceViewer);

        return fSourceViewer.getRangeIndication();
    }

    /*
     * @see ITextEditor#resetHighlightRange
     */
    public void resetHighlightRange() {
        if (fSourceViewer == null)
            return;

        if (fShowHighlightRangeOnly)
            fSourceViewer.resetVisibleRegion();
        else
            fSourceViewer.removeRangeIndication();
    }

    /**
     * Adjusts the highlight range so that at least the specified range
     * is highlighted.
     * <p>
     * Subclasses may re-implement this method.</p>
     *
     * @param offset the offset of the range which at least should be highlighted
     * @param length the length of the range which at least should be highlighted
     */
    protected void adjustHighlightRange(int offset, int length) {
        if (fSourceViewer == null)
            return;

        if (fSourceViewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
            extension.exposeModelRange(new Region(offset, length));
        } else if (!isVisible(fSourceViewer, offset, length)) {
            fSourceViewer.resetVisibleRegion();
        }
    }

    /*
     * @see ITextEditor#selectAndReveal(int, int)
     */
    public void selectAndReveal(int start, int length) {
        selectAndReveal(start, length, start, length);
    }

    /**
     * Selects and reveals the specified ranges in this text editor.
     *
     * @param selectionStart the offset of the selection
     * @param selectionLength the length of the selection
     * @param revealStart the offset of the revealed range
     * @param revealLength the length of the revealed range
     * @since 3.0
     */
    protected void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {
        if (fSourceViewer == null)
            return;

        ISelection selection= getSelectionProvider().getSelection();
        if (selection instanceof ITextSelection) {
            ITextSelection textSelection= (ITextSelection) selection;
            if (textSelection.getOffset() != 0 || textSelection.getLength() != 0)
                markInNavigationHistory();
        }

        StyledText widget= fSourceViewer.getTextWidget();
        widget.setRedraw(false);
        {
            adjustHighlightRange(revealStart, revealLength);
            fSourceViewer.revealRange(revealStart, revealLength);

            fSourceViewer.setSelectedRange(selectionStart, selectionLength);

            markInNavigationHistory();
        }
        widget.setRedraw(true);
    }

    /*
     * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
     * @since 2.1
     */
    public INavigationLocation createEmptyNavigationLocation() {
        return new TextSelectionNavigationLocation(this, false);
    }

    /*
     * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
     */
    public INavigationLocation createNavigationLocation() {
        return new TextSelectionNavigationLocation(this, true);
    }

    /**
     * Writes a check mark of the given situation into the navigation history.
     * @since 2.1
     */
    protected void markInNavigationHistory() {
        getSite().getPage().getNavigationHistory().markLocation(this);
    }

    /**
     * Hook which gets called when the editor has been saved.
     * Subclasses may extend.
     * @since 2.1
     */
    protected void editorSaved() {
        INavigationLocation[] locations= getSite().getPage().getNavigationHistory().getLocations();
        IEditorInput input= getEditorInput();
        for (int i= 0; i < locations.length; i++) {
            if (locations[i] instanceof TextSelectionNavigationLocation) {
                if(input.equals(locations[i].getInput())) {
                    TextSelectionNavigationLocation location= (TextSelectionNavigationLocation) locations[i];
                    location.partSaved(this);
                }
            }
        }
    }

    /*
     * @see WorkbenchPart#firePropertyChange(int)
     */
    protected void firePropertyChange(int property) {
        super.firePropertyChange(property);
        updatePropertyDependentActions();
    }

    /*
     * @see ITextEditorExtension#setStatusField(IStatusField, String)
     * @since 2.0
     */
    public void setStatusField(IStatusField field, String category) {
        Assert.isNotNull(category);
        if (field != null) {

            if (fStatusFields == null)
                fStatusFields= new HashMap (3);

            fStatusFields.put(category, field);
            updateStatusField(category);

        } else if (fStatusFields != null)
            fStatusFields.remove(category);

        if (fIncrementalFindTarget != null && ITextEditorActionConstants.STATUS_CATEGORY_FIND_FIELD.equals(category))
            fIncrementalFindTarget.setStatusField(field);
    }

    /**
     * Returns the current status field for the given status category.
     *
     * @param category the status category
     * @return the current status field for the given status category
     * @since 2.0
     */
    protected IStatusField getStatusField(String category) {
        if (category != null && fStatusFields != null)
            return (IStatusField) fStatusFields.get(category);
        return null;
    }

    /**
     * Returns whether this editor is in overwrite or insert mode.
     *
     * @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode
     * @since 2.0
     */
    protected boolean isInInsertMode() {
        return !fIsOverwriting;
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension3#getInsertMode()
     * @since 3.0
     */
    public InsertMode getInsertMode() {
        return fInsertMode;
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension3#setInsertMode(org.eclipse.ui.texteditor.ITextEditorExtension3.InsertMode)
     * @since 3.0
     */
    public void setInsertMode(InsertMode newMode) {
        List legalModes= getLegalInsertModes();
        if (!legalModes.contains(newMode))
            throw new IllegalArgumentException ();

        fInsertMode= newMode;

        handleInsertModeChanged();
    }

    /**
     * Returns the set of legal insert modes. If insert modes are configured all defined insert modes
     * are legal.
     *
     * @return the set of legal insert modes
     * @since 3.0
     */
    protected List getLegalInsertModes() {
        if (fLegalInsertModes == null) {
            fLegalInsertModes= new ArrayList ();
            fLegalInsertModes.add(SMART_INSERT);
            fLegalInsertModes.add(INSERT);
        }
        return fLegalInsertModes;
    }

    private void switchToNextInsertMode() {

        InsertMode mode= getInsertMode();
        List legalModes= getLegalInsertModes();

        int i= 0;
        while (i < legalModes.size()) {
            if (legalModes.get(i) == mode) break;
            ++ i;
        }

        i= (i + 1) % legalModes.size();
        InsertMode newMode= (InsertMode) legalModes.get(i);
        setInsertMode(newMode);
    }

    private void toggleOverwriteMode() {
        if (fIsOverwriteModeEnabled) {
            fIsOverwriting= !fIsOverwriting;
            fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
            handleInsertModeChanged();
        }
    }

    /**
     * Configures the given insert mode as legal or illegal. This call is ignored if the set of legal
     * input modes would be empty after the call.
     *
     * @param mode the insert mode to be configured
     * @param legal <code>true</code> if the given mode is legal, <code>false</code> otherwise
     * @since 3.0
     */
    protected void configureInsertMode(InsertMode mode, boolean legal) {
        List legalModes= getLegalInsertModes();
        if (legal) {
            if (!legalModes.contains(mode))
                legalModes.add(mode);
        } else if (legalModes.size() > 1) {
            if (getInsertMode() == mode)
                switchToNextInsertMode();
            legalModes.remove(mode);
        }
    }

    /**
     * Sets the overwrite mode enablement.
     *
     * @param enable <code>true</code> to enable new overwrite mode,
     * <code>false</code> to disable
     * @since 3.0
     */
    protected void enableOverwriteMode(boolean enable) {
        if (fIsOverwriting && !enable)
            toggleOverwriteMode();
        fIsOverwriteModeEnabled= enable;
    }

    private Caret createOverwriteCaret(StyledText styledText) {
        Caret caret= new Caret(styledText, SWT.NULL);
        GC gc= new GC(styledText);
        // XXX this overwrite box is not proportional-font aware
 // take 'a' as a medium sized character
 Point charSize= gc.stringExtent("a"); //$NON-NLS-1$

        // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
 caret.setSize(charSize.x, styledText.getLineHeight());
        caret.setFont(styledText.getFont());
        
        gc.dispose();

        return caret;
    }

    private Caret createInsertCaret(StyledText styledText) {
        Caret caret= new Caret(styledText, SWT.NULL);

        // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
 caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
        caret.setFont(styledText.getFont());
        
        return caret;
    }

    private Image createRawInsertModeCaretImage(StyledText styledText) {

        PaletteData caretPalette= new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
        int width= getCaretWidthPreference();
        int widthOffset= width - 1;
        
        // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
 ImageData imageData= new ImageData(4 + widthOffset, styledText.getLineHeight(), 1, caretPalette);

        Display display= styledText.getDisplay();
        Image bracketImage= new Image(display, imageData);
        GC gc= new GC (bracketImage);
        gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
        gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
 int height= imageData.height / 3;
        // gap between two bars of one third of the height
 // draw boxes using lines as drawing a line of a certain width produces
 // rounded corners.
 for (int i= 0; i < width ; i++) {
            gc.drawLine(i, 0, i, height - 1);
            gc.drawLine(i, imageData.height - height, i, imageData.height - 1);
        }

        gc.dispose();

        return bracketImage;
    }

    private Caret createRawInsertModeCaret(StyledText styledText) {
        // don't draw special raw caret if no smart mode is enabled
 if (!getLegalInsertModes().contains(SMART_INSERT))
            return createInsertCaret(styledText);

        Caret caret= new Caret(styledText, SWT.NULL);
        Image image= createRawInsertModeCaretImage(styledText);
        if (image != null)
            caret.setImage(image);
        else {
            // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
 caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
        }

        caret.setFont(styledText.getFont());

        return caret;
    }

    private int getCaretWidthPreference() {
        if (getPreferenceStore() != null && getPreferenceStore().getBoolean(PREFERENCE_WIDE_CARET))
            return WIDE_CARET_WIDTH;

        return SINGLE_CARET_WIDTH;
    }

    private void updateCaret() {

        if (fSourceViewer == null)
            return;

        StyledText styledText= fSourceViewer.getTextWidget();

        InsertMode mode= getInsertMode();

        styledText.setCaret(null);
        disposeNonDefaultCaret();

        if (getPreferenceStore() == null || !getPreferenceStore().getBoolean(PREFERENCE_USE_CUSTOM_CARETS))
            Assert.isTrue(fNonDefaultCaret == null);
        else if (fIsOverwriting)
            fNonDefaultCaret= createOverwriteCaret(styledText);
        else if (SMART_INSERT == mode)
            fNonDefaultCaret= createInsertCaret(styledText);
        else if (INSERT == mode)
            fNonDefaultCaret= createRawInsertModeCaret(styledText);

        if (fNonDefaultCaret != null) {
            styledText.setCaret(fNonDefaultCaret);
            fNonDefaultCaretImage= fNonDefaultCaret.getImage();
        } else if (fInitialCaret != styledText.getCaret())
            styledText.setCaret(fInitialCaret);
    }

    private void disposeNonDefaultCaret() {
        if (fNonDefaultCaretImage != null) {
            fNonDefaultCaretImage.dispose();
            fNonDefaultCaretImage= null;
        }

        if (fNonDefaultCaret != null) {
            fNonDefaultCaret.dispose();
            fNonDefaultCaret= null;
        }
    }

    /**
     * Handles a change of the editor's insert mode.
     * Subclasses may extend.
     *
     * @since 2.0
     */
    protected void handleInsertModeChanged() {
        updateInsertModeAction();
        updateCaret();
        updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
    }

    private void updateInsertModeAction() {

        // this may be called before the part is fully initialized (see configureInsertMode)
 // drop out in this case.
 if (getSite() == null)
            return;

        IAction action= getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
        if (action != null) {
            action.setEnabled(!fIsOverwriting);
            action.setChecked(fInsertMode == SMART_INSERT);
        }
    }

    /**
     * Handles a potential change of the cursor position.
     * Subclasses may extend.
     *
     * @since 2.0
     */
    protected void handleCursorPositionChanged() {
        updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
    }

    /**
     * Updates the status fields for the given category.
     *
     * @param category
     * @since 2.0
     */
    protected void updateStatusField(String category) {

        if (category == null)
            return;

        IStatusField field= getStatusField(category);
        if (field != null) {

            String text= null;

            if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category))
                text= getCursorPosition();
            else if (ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE.equals(category))
                text= isEditorInputReadOnly() ? fReadOnlyLabel : fWritableLabel;
            else if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE.equals(category)) {
                InsertMode mode= getInsertMode();
                if (fIsOverwriting)
                    text= fOverwriteModeLabel;
                else if (INSERT == mode)
                    text= fInsertModeLabel;
                else if (SMART_INSERT == mode)
                    text= fSmartInsertModeLabel;
            }

            field.setText(text == null ? fErrorLabel : text);
        }
    }

    /**
     * Updates all status fields.
     *
     * @since 2.0
     */
    protected void updateStatusFields() {
        if (fStatusFields != null) {
            Iterator e= fStatusFields.keySet().iterator();
            while (e.hasNext())
                updateStatusField((String ) e.next());
        }
    }

    /**
     * Returns a description of the cursor position.
     *
     * @return a description of the cursor position
     * @since 2.0
     */
    protected String getCursorPosition() {

        if (fSourceViewer == null)
            return fErrorLabel;

        StyledText styledText= fSourceViewer.getTextWidget();
        int caret= widgetOffset2ModelOffset(fSourceViewer, styledText.getCaretOffset());
        IDocument document= fSourceViewer.getDocument();

        if (document == null)
            return fErrorLabel;

        try {

            int line= document.getLineOfOffset(caret);

            int lineOffset= document.getLineOffset(line);
            int tabWidth= styledText.getTabs();
            int column= 0;
            for (int i= lineOffset; i < caret; i++)
                if ('\t' == document.getChar(i))
                    column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth);
                else
                    column++;

            fLineLabel.fValue= line + 1;
            fColumnLabel.fValue= column + 1;
            return NLSUtility.format(fPositionLabelPattern, fPositionLabelPatternArguments);

        } catch (BadLocationException x) {
            return fErrorLabel;
        }
    }

    /*
     * @see ITextEditorExtension#isEditorInputReadOnly()
     * @since 2.0
     */
    public boolean isEditorInputReadOnly() {
        IDocumentProvider provider= getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension) {
            IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
            return extension.isReadOnly(getEditorInput());
        }
        return true;
    }

    /*
     * @see ITextEditorExtension2#isEditorInputModifiable()
     * @since 2.1
     */
    public boolean isEditorInputModifiable() {
        IDocumentProvider provider= getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension) {
            IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
            return extension.isModifiable(getEditorInput());
        }
        return true;
    }

    /*
     * @see ITextEditorExtension#addRulerContextMenuListener(IMenuListener)
     * @since 2.0
     */
    public void addRulerContextMenuListener(IMenuListener listener) {
        fRulerContextMenuListeners.add(listener);
    }

    /*
     * @see ITextEditorExtension#removeRulerContextMenuListener(IMenuListener)
     * @since 2.0
     */
    public void removeRulerContextMenuListener(IMenuListener listener) {
        fRulerContextMenuListeners.remove(listener);
    }

    /**
     * Returns whether this editor can handle the move of the original element
     * so that it ends up being the moved element. By default this method
     * returns <code>true</code>. Subclasses may reimplement.
     *
     * @param originalElement the original element
     * @param movedElement the moved element
     * @return whether this editor can handle the move of the original element
     * so that it ends up being the moved element
     * @since 2.0
     */
    protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
        return true;
    }

    /**
     * Returns the offset of the given source viewer's document that corresponds
     * to the given widget offset or <code>-1</code> if there is no such offset.
     *
     * @param viewer the source viewer
     * @param widgetOffset the widget offset
     * @return the corresponding offset in the source viewer's document or <code>-1</code>
     * @since 2.1
     */
    protected final static int widgetOffset2ModelOffset(ISourceViewer viewer, int widgetOffset) {
        if (viewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
            return extension.widgetOffset2ModelOffset(widgetOffset);
        }
        return widgetOffset + viewer.getVisibleRegion().getOffset();
    }

    /**
     * Returns the offset of the given source viewer's text widget that corresponds
     * to the given model offset or <code>-1</code> if there is no such offset.
     *
     * @param viewer the source viewer
     * @param modelOffset the model offset
     * @return the corresponding offset in the source viewer's text widget or <code>-1</code>
     * @since 3.0
     */
    protected final static int modelOffset2WidgetOffset(ISourceViewer viewer, int modelOffset) {
        if (viewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
            return extension.modelOffset2WidgetOffset(modelOffset);
        }
        return modelOffset - viewer.getVisibleRegion().getOffset();
    }

    /**
     * Returns the minimal region of the given source viewer's document that completely
     * comprises everything that is visible in the viewer's widget.
     *
     * @param viewer the viewer go return the coverage for
     * @return the minimal region of the source viewer's document comprising the contents of the viewer's widget
     * @since 2.1
     */
    protected final static IRegion getCoverage(ISourceViewer viewer) {
        if (viewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
            return extension.getModelCoverage();
        }
        return viewer.getVisibleRegion();
    }

    /**
     * Tells whether the given region is visible in the given source viewer.
     *
     * @param viewer the source viewer
     * @param offset the offset of the region
     * @param length the length of the region
     * @return <code>true</code> if visible
     * @since 2.1
     */
    protected static final boolean isVisible(ISourceViewer viewer, int offset, int length) {
        if (viewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
            IRegion overlap= extension.modelRange2WidgetRange(new Region(offset, length));
            return overlap != null;
        }
        return viewer.overlapsWithVisibleRegion(offset, length);
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension3#showChangeInformation(boolean)
     * @since 3.0
     */
    public void showChangeInformation(boolean show) {
        // do nothing
 }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension3#isChangeInformationShowing()
     * @since 3.0
     */
    public boolean isChangeInformationShowing() {
        return false;
    }
    
    /**
     * Sets the given message as error message to this editor's status line.
     *
     * @param message message to be set
     * @since 3.2
     */
    protected void setStatusLineErrorMessage(String message) {
        IEditorStatusLine statusLine= (IEditorStatusLine)getAdapter(IEditorStatusLine.class);
        if (statusLine != null)
            statusLine.setMessage(true, message, null);
    }

    /**
     * Sets the given message as message to this editor's status line.
     *
     * @param message message to be set
     * @since 3.2
     */
    protected void setStatusLineMessage(String message) {
        IEditorStatusLine statusLine= (IEditorStatusLine)getAdapter(IEditorStatusLine.class);
        if (statusLine != null)
            statusLine.setMessage(false, message, null);
    }
    
    /**
     * Jumps to the next annotation according to the given direction.
     *
     * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
     * @return the selected annotation or <code>null</code> if none
     * @see #isNavigationTarget(Annotation)
     * @see #findAnnotation(int, int, boolean, Position)
     * @since 3.2
     */
    public Annotation gotoAnnotation(boolean forward) {
        ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
        Position position= new Position(0, 0);
        Annotation annotation= findAnnotation(selection.getOffset(), selection.getLength(), forward, position);
        setStatusLineErrorMessage(null);
        setStatusLineMessage(null);
        
        if (annotation != null) {
            selectAndReveal(position.getOffset(), position.getLength());
            setStatusLineMessage(annotation.getText());
        }
        return annotation;
    }
    
    /**
     * Returns the annotation closest to the given range respecting the given
     * direction. If an annotation is found, the annotations current position
     * is copied into the provided annotation position.
     *
     * @param offset the region offset
     * @param length the region length
     * @param forward <code>true</code> for forwards, <code>false</code> for backward
     * @param annotationPosition the position of the found annotation
     * @return the found annotation
     * @since 3.2
     */
    protected Annotation findAnnotation(final int offset, final int length, boolean forward, Position annotationPosition) {

        Annotation nextAnnotation= null;
        Position nextAnnotationPosition= null;
        Annotation containingAnnotation= null;
        Position containingAnnotationPosition= null;
        boolean currentAnnotation= false;

        IDocument document= getDocumentProvider().getDocument(getEditorInput());
        int endOfDocument= document.getLength();
        int distance= Integer.MAX_VALUE;

        IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
        Iterator e= model.getAnnotationIterator();
        while (e.hasNext()) {
            Annotation a= (Annotation) e.next();
            if (!isNavigationTarget(a))
                continue;

            Position p= model.getPosition(a);
            if (p == null)
                continue;

            if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
 if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) {
                    containingAnnotation= a;
                    containingAnnotationPosition= p;
                    currentAnnotation= p.length == length;
                }
            } else {
                int currentDistance= 0;

                if (forward) {
                    currentDistance= p.getOffset() - offset;
                    if (currentDistance < 0)
                        currentDistance= endOfDocument + currentDistance;

                    if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
                        distance= currentDistance;
                        nextAnnotation= a;
                        nextAnnotationPosition= p;
                    }
                } else {
                    currentDistance= offset + length - (p.getOffset() + p.length);
                    if (currentDistance < 0)
                        currentDistance= endOfDocument + currentDistance;

                    if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
                        distance= currentDistance;
                        nextAnnotation= a;
                        nextAnnotationPosition= p;
                    }
                }
            }
        }
        if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) {
            annotationPosition.setOffset(containingAnnotationPosition.getOffset());
            annotationPosition.setLength(containingAnnotationPosition.getLength());
            return containingAnnotation;
        }
        if (nextAnnotationPosition != null) {
            annotationPosition.setOffset(nextAnnotationPosition.getOffset());
            annotationPosition.setLength(nextAnnotationPosition.getLength());
        }

        return nextAnnotation;
    }

    /**
     * Returns whether the given annotation is configured as a target for the
     * "Go to Next/Previous Annotation" actions.
     * <p>
     * Per default every annotation is a target.
     * </p>
     *
     * @param annotation the annotation
     * @return <code>true</code> if this is a target, <code>false</code> otherwise
     * @since 3.2
     */
    protected boolean isNavigationTarget(Annotation annotation) {
        return true;
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditorExtension4#showRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation, java.lang.String)
     * @since 3.2
     */
    public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
        // no implementation
 }
    
    /*
     * @see org.eclipse.ui.IEditorPersistable#restoreState(org.eclipse.ui.IMemento)
     * @since 3.3
     */
    public void restoreState(IMemento memento) {
        fMementoToRestore= memento;
    }
    
    /*
     * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)
     * @since 3.3
     */
    public void saveState(IMemento memento) {
        ISelection selection= doGetSelection();
        if (selection instanceof ITextSelection) {
            memento.putInteger(TAG_SELECTION_OFFSET, ((ITextSelection)selection).getOffset());
            memento.putInteger(TAG_SELECTION_LENGTH, ((ITextSelection)selection).getLength());
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
 memento.putInteger(TAG_SELECTION_HPIXEL, getSourceViewer().getTextWidget().getHorizontalPixel());
        }
    }
    
    /**
     * Returns whether the given memento contains saved state
     * <p>
     * Subclasses may extend or override this method.</p>
     *
     * @param memento the saved state of this editor
     * @return <code>true</code> if the given memento contains saved state
     * @since 3.3
     */
    protected boolean containsSavedState(IMemento memento) {
        return memento.getInteger(TAG_SELECTION_OFFSET) != null && memento.getInteger(TAG_SELECTION_LENGTH) != null;
    }
    
    /**
     * Restores this editor's state using the given memento.
     * <p>
     * Subclasses may extend or override this method.</p>
     *
     * @param memento the saved state of this editor
     * @since 3.3
     */
    protected void doRestoreState(IMemento memento) {
        Integer offset= memento.getInteger(TAG_SELECTION_OFFSET);
        if (offset == null)
            return;
        
        Integer length= memento.getInteger(TAG_SELECTION_LENGTH);
        if (length == null)
            return;
        
        doSetSelection(new TextSelection(offset.intValue(), length.intValue()));
        
        // XXX: https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
 Integer horizontalPixel= memento.getInteger(TAG_SELECTION_HPIXEL);
        if (horizontalPixel == null)
            return;
        StyledText textWidget= getSourceViewer().getTextWidget();
        if (!textWidget.isVisible())
            textWidget.setHorizontalPixel(horizontalPixel.intValue());
    }
    
    /*
     * @see org.eclipse.ui.ISaveablesSource#getSaveables()
     * @since 3.3
     */
    public Saveable[] getSaveables() {
        if (fSavable == null)
            fSavable= new TextEditorSavable(this);

        return new Saveable[] { fSavable };
    }

    /*
     * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
     * @since 3.3
     */
    public Saveable[] getActiveSaveables() {
        return getSaveables();
    }
    
    /**
     * This text editor's savable.
     *
     * @since 3.3
     */
    protected static class TextEditorSavable extends Saveable {
        
        /** The cached editor. */
        private ITextEditor fTextEditor;
        /** The cached editor input. */
        private IEditorInput fEditorInput;
        /** The cached document. */
        private IDocument fDocument;

        /**
         * Creates a new savable for this text editor.
         *
         * @param textEditor the text editor
         */
        public TextEditorSavable(ITextEditor textEditor) {
            Assert.isLegal(textEditor != null);
            fTextEditor= textEditor;
            fEditorInput= fTextEditor.getEditorInput();
            Assert.isLegal(fEditorInput != null);
        }

        /**
         * Disconnects the editor from this savable.
         */
        public void disconnectEditor() {
            getAdapter(IDocument.class); // make sure the document is cached
 fTextEditor= null;
        }

        /*
         * @see org.eclipse.ui.Saveable#getName()
         */
        public String getName() {
            return fEditorInput.getName();
        }

        /*
         * @see org.eclipse.ui.Saveable#getToolTipText()
         */
        public String getToolTipText() {
            return fEditorInput.getToolTipText();
        }

        /*
         * @see org.eclipse.ui.Saveable#getImageDescriptor()
         */
        public ImageDescriptor getImageDescriptor() {
            return fEditorInput.getImageDescriptor();
        }

        /*
         * @see org.eclipse.ui.Saveable#doSave(org.eclipse.core.runtime.IProgressMonitor)
         * @since 3.3
         */
        public void doSave(IProgressMonitor monitor) throws CoreException {
            fTextEditor.doSave(monitor);
        }

        public boolean isDirty() {
            return fTextEditor.isDirty();
        }

        /*
         * @see org.eclipse.ui.Saveable#supportsBackgroundSave()
         */
        public boolean supportsBackgroundSave() {
            return false;
        }

        /*
         * @see org.eclipse.ui.Saveable#hashCode()
         */
        public int hashCode() {
            Object document= getAdapter(IDocument.class);
            if (document == null)
                return 0;
            return document.hashCode();
        }

        /*
         * @see org.eclipse.ui.Saveable#equals(java.lang.Object)
         */
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            
            if (!(obj instanceof Saveable))
                return false;
            
            Object thisDocument= getAdapter(IDocument.class);
            Object otherDocument= ((Saveable)obj).getAdapter(IDocument.class);
            
            if (thisDocument == null && otherDocument == null)
                return true;
            
            return thisDocument != null && thisDocument.equals(otherDocument);
        }

        /**
         * Explicit comment needed to suppress wrong waning caused
         * by http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4848177
         *
         * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
         */
        public Object getAdapter(Class adapter) {
            if (adapter == IDocument.class) {
                if (fDocument == null) {
                    IDocumentProvider documentProvider= fTextEditor.getDocumentProvider();
                    if (documentProvider != null)
                        fDocument= documentProvider.getDocument(fEditorInput);
                }
                return fDocument;
            }
            return super.getAdapter(adapter);
        }
    }
    
    //---- Tabs to spaces conversion support ------------------

    /**
     * Installs a tabs to spaces converter.
     *
     * <p>Subclasses may extend or override this method.</p>
     *
     * @since 3.3
     */
    protected void installTabsToSpacesConverter() {
        SourceViewerConfiguration config= getSourceViewerConfiguration();
        if (config != null && fSourceViewer instanceof ITextViewerExtension7) {
            int tabWidth= config.getTabWidth(fSourceViewer);
            TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
            tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
            tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
            ((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
            updateIndentPrefixes();
        }
    }

    /**
     * Installs a tabs to spaces converter.
     *
     * <p>Subclasses may extend or override this method.</p>
     *
     * @since 3.3
     */
    protected void uninstallTabsToSpacesConverter() {
        if (fSourceViewer instanceof ITextViewerExtension7) {
            ((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(null);
            if (fSourceViewer.getTextWidget() != null)
                updateIndentPrefixes();
        }
    }

    /**
     * Tells whether tabs should be converted to
     * spaces while editing inside this editor.
     *
     * <p>Subclasses may override this method.</p>
     *
     * @return <code>true</code> if tabs should be converted to spaces
     * @since 3.3
     */
    protected boolean isTabsToSpacesConversionEnabled() {
        return false;
    }

    /**
     * Updates the source viewer's indent prefixes with
     * the values provided by the source viewer configuration.
     *
     * @since 3.3
     */
    protected final void updateIndentPrefixes() {
        SourceViewerConfiguration configuration= getSourceViewerConfiguration();
        String [] types= configuration.getConfiguredContentTypes(fSourceViewer);
        for (int i= 0; i < types.length; i++) {
            String [] prefixes= configuration.getIndentPrefixes(fSourceViewer, types[i]);
            if (prefixes != null && prefixes.length > 0)
                fSourceViewer.setIndentPrefixes(prefixes, types[i]);
        }
    }

}

